Basic acoustics#
- echopop.inversion.wavenumber(frequency: ndarray[float] | float, sound_speed_sw: float) ndarray[float]#
Compute the acoustic wavenumber from frequency and sound speed.
The acoustic wavenumber relates frequency and wavelength through the medium’s sound speed, fundamental to acoustic scattering calculations.
- Parameters:
- frequencyUnion[numpy.ndarray[float], float]
Acoustic frequency in Hz. Can be scalar or array for multiple frequencies.
- sound_speed_swfloat
Sound speed in seawater in m s \(^{-1}\), typically ~1500 m s \(^{-1}\) depending on temperature, salinity, and pressure conditions.
- Returns:
- numpy.ndarray[float]
Acoustic wavenumber in rad m \(^{-1}\). Same shape as input frequency.
Notes
The wavenumber is calculated as:
\[\begin{split}k = \\frac{2\\pi f}{c}\end{split}\]where \(k\) is wavenumber (rad m \(^{-1}\)), \(f\) is frequency (Hz), and \(c\) is sound speed (m s \(^{-1}\)).
References
[1]Medwin, H. & Clay, C.S. (1998). Fundamentals of Acoustical Oceanography. Academic Press.
Examples
>>> # Single frequency >>> k = wavenumber(120e3, 1500.0) # 120 kHz in 1500 m/s water >>> print(f"k = {k:.2f} rad/m") k = 502.65 rad/m
>>> # Multiple frequencies >>> freqs = np.array([18e3, 38e3, 120e3]) >>> k_array = wavenumber(freqs, 1500.0)
- echopop.inversion.reflection_coefficient(g: ndarray[float] | float, h: ndarray[float] | float) ndarray[float]#
Compute the acoustic reflection coefficient from material properties.
The reflection coefficient quantifies acoustic impedance mismatch between organism tissue and s urrounding seawater, crucial for scattering strength calculations in biological acoustic models.
- Parameters:
- gUnion[numpy.ndarray[float], float]
The density contrast, which is the ratio of organism density to seawater density (kg m \(^{-3}\)). For fluid-like, weak scatterers, values for zooplankton are often close to unity (1.00), typically 1.00-1.05.
- hUnion[numpy.ndarray[float], float]
The sound speed contrast, which is the ratio of organism sound speed to seawater sound speed (m s \(^{-1}\)). For fluid-like, weak scatterers, values for zooplankton are often close to unity (1.00), typically 1.00-1.05.
- Returns:
- numpy.ndarray[float]
Acoustic reflection coefficient (\(\\mathscr{R}_{12}\), dimensionless) with the same shape as the inputs.
Notes
Let the material properties of the surrounding seawater be denoted as \((\\rho_1, c_1)\) and those of the scatterer \((\\rho_2, c_2)\). The density contrast \(g\) and sound speed contrast \(h\) are defined as:
\[\begin{split}g = \\frac{\\rho_2}{\\rho_1}, \\quad h = \\frac{c_2}{c_1}\end{split}\]These are then used to compute the reflection coefficient \(\\mathscr{R}_{12}\):
\[\begin{split}\\mathscr{R}_{12} = \\frac{1 - gh^2}{gh^2} - \\frac{g-1}{g}\end{split}\]where \(\\mathscr{R}_{12}\) is dimensionless. This quantity constitutes a simplification of the boundary conditions at the interface between the surrounding seawater and scatterer. It encapsulates how acoustic waves are partially reflected an transmitted due to this impedance mismatch.
Examples
>>> # Typical krill parameters >>> g = 1.04 # 4% denser than seawater >>> h = 1.02 # 2% faster sound speed than seawater >>> R = reflection_coefficient(g, h) >>> print(f"Reflection coefficient: {R:.4f}")
>>> # Array inputs for parameter studies >>> g_vals = np.linspace(1.01, 1.08, 5) >>> h_vals = np.linspace(1.00, 1.04, 5) >>> R_matrix = reflection_coefficient(g_vals[:, None], h_vals)
- echopop.inversion.length_average(length_values: ndarray[float], ka_f: ndarray[float], ka_c: ndarray[float], form_function: ndarray[complex] | ndarray[float], length_mean: float, length_deviation: float, distribution: Literal['gaussian', 'uniform'] = 'gaussian', output_type: Literal['sigma_bs', 'f_bs'] = 'sigma_bs', convert_type: bool = True) list[ndarray[float]]#
Compute the length-averaged backscattering cross-section or linear scattering coefficient.
Computes length-averaged backscattering cross-section (\(\\sigma_\\text{bs}(f, L)\)) or linear scattinering coefficient \(f_\\text{bs}\) (m) or backscattering cross-section \(\\sigma_\\text{bs}\) (m²).
This function integrates the form function, \(\\mathscr{f}(f, L)\), over the scatterer’s length distribution. This effectively weights \(\\mathscr{f}(f, L)\) using a probability density function (PDF) to account for natural variation in scatterer length in the water column. The result is the length-averaged form function \(\\bar{\\mathscr{f}}(f)\).
- Parameters:
- length_valuesnumpy.ndarray[float]
Array of scatterer lengths (\(L\), m).
- ka_fnumpy.ndarray[float]
Array of dimensionless wavenumber-length products for each frequency (ka).
- ka_cnumpy.ndarray[float]
Array of reference ka values for each length, typically computed from central frequency.
- form_functionnumpy.ndarray[float] or numpy.ndarray[float]
The scattering form function, either the complex linear scattering coefficient (\(f_\\text{bs}(f, L)\)) or backscattering cross-section (\(\\sigma_\\text{bs}(f, L)\)).
- length_meanfloat
Mean scatterer length (\(\\bar{L}\), m).
- length_deviationfloat
Standard deviation of scatterer lengths (\(\\sigma_L\), m).
- distributionLiteral[“gaussian”, “uniform”], default=”gaussian”
Length probability distribution function (PDF) used for averaging. There are two available options:
"gaussian": Normal/Gaussian distribution around \(\\bar{L}\) (length_mean). This is the default distribution, and requires bothlength_meanandlength_sdto be specified."uniform": Uniform distribution over all orientation values inlength_values.
- output_typeLiteral[“sigma_bs”, “f_bs”], default=”sigma_bs”
Specifies the desired output type after length averaging:
"sigma_bs": Outputs the length-averaged backscattering cross-section (\(\\sigma_\\text{bs}(f)\), m²). Whenform_functionis complex, it is converted to \(\\sigma_\\text{bs}\) before averaging whenconvert_typeis True. This is the default option."f_bs": Outputs the length-averaged linear scattering coefficient (\(f_\\text{bs}(f)\), m).
- convert_typebool, default=True
If
Trueandoutput_typeis set to"sigma_bs", the function converts the inputform_functionfrom complex linear scattering coefficient (\(f_\\text{bs}\)) to backscattering cross-section (\(\\sigma_\\text{bs}\)) before averaging. IfFalse, the function assumes the inputform_functionis already in the desired output type.
- Returns:
- List[numpy.ndarray[float]]
Length-averaged backscattering cross-section \(\\sigma_\\text{bs}\) (m²). When
form_functionis complex, it is converted to \(\\sigma_\\text{bs}\) before averaging. The output quantity yields a single length-averaged \(\\sigma_\\text{bs}\) value for each frequency.
Notes
The length averaging when \(\\mathscr{f}(f, L)\) is real is defined as:
\[\begin{split}\\bar{\\mathscr{f}}^*(f) = \\int \\mathscr{f}(f, L) P(L^*) dL^*\end{split}\]where \(\\mathscr{f}(f, L)\) is the form function at frequency \(f\) and normalized length \(L^*\), and \(P(L^*)\) is the normalized probability density function (PDF) such that:
\[\begin{split}P(L^*) = \\frac{P(L)}{\\bar{L}}\end{split}\]Since \(\\mathscr{f}(f, L)\) can also be complex, the form function can be modified to calculate the squared magnitude:
\[\begin{split}\\bar{\\mathscr{f}}^*(f) = \\int (|\\mathscr{f}(f, L)|^2) P(L^*) dL^*\end{split}\]For Gaussian distributions, the PDF is defined as:
\[\begin{split}P(L^*) = \\frac{1}{\\sqrt{2\\pi}\\sigma_{L^*}} \\exp\\left(-\\frac{(L^*-\\bar{L^*})^2}{2\\sigma_{L^*}^2}\\right)\end{split}\]For uniform distributions, the PDF is constant over the range of \(L^*\):
\[\begin{split}P(L^*) = \\frac{1}{n_{L^*}}\end{split}\]where \(n_{L^*}\) is the number of discrete lengths in \(L^*\).
For computational purposes, this integral is approximated using a discrete sum over \(L^*\):
\[\begin{split}\\begin{align*} \\bar{\\mathscr{f}}^*(f) &\\approx \\sum_{L^*} |\\mathscr{f}(f, L)| P(L^*) \\Delta L^* \\quad \\text{when } \\bar{\\mathscr{f}}^*(f) \\rightarrow \\bar{\\mathscr{f}}^*(f) \\\\ \\bar{\\mathscr{f}}^*(f) &\\approx \\sum_{L^*} |\\mathscr{f}(f, L)|^2 P(L^*) \\Delta L^* \\quad \\text{when } \\bar{f}_\\text{bs}(f) \\rightarrow \\bar{\\sigma}_\\text{bs}^*(f) \\end{align*}\end{split}\]where \(\\Delta L^*\) is the interval between discrete length values in \(L^*\). In the case of the bottom equation, the squared magnitude is used when converting the mean linear scattering coefficient \(\\bar{f}_\\text{bs}(f)\) to the mean backscattering cross-section \(\\bar{\\sigma}_\\text{bs}^*(f)\). Because \(\\bar{\\sigma}_\\text{bs}^*(f)\) is normalized to length, it is in units of m, not m². Consequently, an additional scaling is required to convert to the true mean backscattering cross-section \(\\bar{\\sigma}_\\text{bs}(f)\) in m²:
\[\begin{split}\\bar{\\sigma}_\\text{bs}(f) = \\bar{\\sigma}_\\text{bs}^*(f) \\times \\bar{L}^2\end{split}\]Examples
>>> lengths = np.linspace(0.01, 0.03, 10) # 1 to 3 cm >>> sigma_bs_L = length_average(lengths, ka_f, ka_c, form_func, 0.02, 0.005)
- echopop.inversion.orientation_average(theta: ndarray[float], form_function: ndarray[complex] | ndarray[float], theta_mean: float | None = None, theta_sd: float | None = None, distribution: Literal['gaussian', 'uniform'] = 'gaussian', output_type: Literal['sigma_bs', 'f_bs'] = 'sigma_bs', convert_type: bool = True) list[ndarray[float]]#
Compute the orientation-averaged form function for a scattering coefficient or cross-section.
Computes the orientation-averaged form function (\(\\mathscr{f}(f, \\theta)\)) for complex linear scattering coefficient \(f_\\text{bs}\) or backscattering cross-section \(\\sigma_\\text{bs}\).
This function integrates the form function, \(\\mathscr{f}(f, \\theta)\), over the scatterer’s orientation distribution. This effectively weights \(\\mathscr{f}(f, \\theta)\) using a probability density function (PDF) to account for natural variation in scatterer orientation in the water column. The result is the orientation-averaged form function \(\\bar{\\mathscr{f}}(f)\).
- Parameters:
- thetanumpy.ndarray[float]
Array of orientation angles (\(\\theta\), °) in degrees, typically relative to the incident acoustic wave where broadside incidence is considered to be 90°.
- form_functionnumpy.ndarray[complex] or numpy.ndarray[float]
The scattering form function that is either the complex linear scattering coefficient (\(f_\\text{bs}(f, \\theta)\)) or backscattering cross-section (\(\\sigma_\\text{bs}(f, \\theta)\)). This must be an a complex or float array with the same shape as the array for
theta.- theta_meanOptional[float]
Mean orientation angle (\(\\bar{\\theta}\), °) in degrees.
- theta_sdOptional[float]
Standard deviation of orientation values (\(\\sigma_{\\theta}\), °).
- distributionLiteral[“gaussian”, “uniform”], default=”gaussian”
Orientation probability distribution function (PDF) used for averaging. There are two available options:
"gaussian": Normal/Gaussian distribution around \(\\bar{\\theta}\) (theta_mean). This is the default distribution, and requires boththeta_meanandtheta_sdto be specified."uniform": Uniform distribution over all orientation values intheta.
- output_typeLiteral[“sigma_bs”, “f_bs”], default=”sigma_bs”
Specifies the desired output type after length averaging:
"sigma_bs": Outputs the length-averaged backscattering cross-section (\(\\sigma_\\text{bs}(f)\), m²). Whenform_functionis complex, it is converted to \(\\sigma_\\text{bs}\) before averaging before averaging whenconvert_typeis True. This is the default option."f_bs": Outputs the length-averaged linear scattering coefficient (\(f_\\text{bs}(f)\), m).
- convert_typebool, default=True
If
Trueandoutput_typeis set to"sigma_bs", the function converts the inputform_functionfrom complex linear scattering coefficient (\(f_\\text{bs}\)) to backscattering cross-section (\(\\sigma_\\text{bs}\)) before averaging. IfFalse, the function assumes the inputform_functionis already in the desired output type.
- Returns:
- List[numpy.ndarray[float]]
Orientation-averaged backscattering cross-section \(\\sigma_\\text{bs}\) (m²). When
form_functionrepresents the linear scattering coefficient (i.e., a complex array), the output remains \(f_\\text{bs}\) after averaging.
Notes
The orientation averaging when \(\\mathscr{f}(f, \\theta)\) is real is defined as:
\[\begin{split}\\bar{\\mathscr{f}}(f) = \\int \\mathscr{f}(f, \\theta) P(\\theta) d\\theta\end{split}\]where \(\\mathscr{f}(f, \\theta)\) is the form function at frequency \(f\) and orientation \(\\theta\), and \(P(\\theta)\) is the probability density function (PDF). Since \(\\mathscr{f}(f, \\theta)\) can also be the linear scattering coefficient, the averaged form function calculation is modified to account for this, defined as:
\[\begin{split}\\bar{\\mathscr{f}}(f) = \\sqrt{ \\int (|\\mathscr{f}(f, \\theta)|^2) P(\\theta) d\\theta }\end{split}\]which corresponds to the real-valuted root mean square (RMS). When using a Gaussian distribution, the PDF is defined as:
\[\begin{split}P(\\theta) = \\frac{1}{\\sqrt{2\\pi}\\sigma_{\\theta}} \\exp\\left(-\\frac{(\\theta-\\bar{\\theta})^2}{2\\sigma_{\\theta}^2}\\right)\end{split}\]When using a uniform distribution, the PDF is constant over the range of \(\\theta\):
\[\begin{split}P(\\theta) = \\frac{1}{n_\\theta}\end{split}\]where \(n_\\theta\) is the number of discrete orientations in \(\\theta\).
For computational purposes, this integral is approximated using a discrete sum over \(\\theta\):
\[\begin{split}\\begin{align*} \\bar{\\mathscr{f}}(f) &\\approx \\sum\\limits_{\\theta} \\mathscr{f}(f, \\theta) P(\\theta) \\Delta\\theta \\quad \\text{when } \\bar{\\mathscr{f}}(f) \\rightarrow \\bar{\\mathscr{f}}(f) \\\\ \\bar{\\mathscr{f}}(f) &\\approx \\sqrt{ \\sum\\limits_{\\theta} |\\mathscr{f}(f, \\theta)|^2 P(\\theta) \\Delta\\theta } \\quad \\text{when } \\mathscr{f}(f, L) \\in \\mathbb{C} \\text{ and } \\bar{\\mathscr{f}}(f) \\rightarrow \\bar{\\mathscr{f}}(f) \\\\ \\bar{\\mathscr{f}}(f) &\\approx \\sum\\limits_{\\theta} |\\mathscr{f}(f, \\theta)|^2 P(\\theta) \\Delta\\theta \\quad \\text{when } \\bar{f}_\\text{bs}(f) \\rightarrow \\bar{\\sigma}_\\text{bs}(f) \\\\ \\end{align*}\end{split}\]where \(\\Delta\\theta\) is the interval between discrete orientation angles in \(\\theta\). In the case of the bottom equation, the squared magnitude is used when converting the mean linear scattering coefficient \(\\bar{f}_\\text{bs}(f)\) to the mean backscattering cross-section \(\\bar{\\sigma}_\\text{bs}(f)\).
Examples
>>> angles = np.linspace(60, 120, 61) # ±30° around vertical >>> # form_function computed from scattering model >>> sigma_bs = orientation_average(angles, form_func, 90.0, 10.0)
- echopop.inversion.impute_missing_sigma_bs(unique_strata: list | ndarray[number], sigma_bs_df: DataFrame) DataFrame#
Imputes \(\\sigma_\\text{bs}\) for strata without measurements or values.
- Parameters:
- unique_strataUnion[list, numpy.ndarray[numpy.number]]
An array comprising all expected stratum numbers.
- sigma_bs_dfpandas.DataFrame
DataFrame containing the mean \(\\sigma_\\text{bs}\) calculated for each stratum. This DataFrame must be indexed by the stratum and must contain the column
'sigma_bs'.
- Returns:
- pandas.DataFrame
DataFrame with imputed \(\\sigma_\\text{bs}\) values for missing strata
Notes
This function iterates through all stratum layers to impute either the nearest neighbor interpolation or mean \(\\sigma_\\text{bs}\) for strata that are missing values.
For missing strata, the function:
Finds the nearest stratum below and above the missing stratum
Interpolates the \(\\sigma_\\text{bs}\) value as the mean of these neighbors
If no neighbors exist on one side, uses the available neighbor value
Examples
>>> # Create sigma_bs data for some strata >>> sigma_bs_df = pd.DataFrame({ ... 'sigma_bs': [0.001, 0.003, 0.002] ... }, index=pd.Index([1, 3, 5], name='stratum')) >>> >>> # Define all expected strata >>> all_strata = [1, 2, 3, 4, 5] >>> >>> # Impute missing strata (2 and 4) >>> result = impute_missing_sigma_bs(all_strata, sigma_bs_df) >>> print(result) sigma_bs stratum 1 0.001000 2 0.002000 # interpolated between 1 and 3 3 0.003000 4 0.002500 # interpolated between 3 and 5 5 0.002000
- echopop.inversion.pcdwba.uniformly_bent_cylinder(n_segments: int | ndarray[int], radius_of_curvature_ratio: float, taper_order: float) DataFrame#
Generate geometric parameters for uniformly bent cylinder with tapered ends.
This function computes the spatial discretization and geometric properties of organisms modeled as uniformly bent cylinders with tapering. It calculates position vectors, orientation angles, and tapering.
- Parameters:
- n_segmentsUnion[int, numpy.ndarray[int]]
Number of integration segments along the longitudinal axis of the scatterer.
- radius_of_curvature_ratiofloat
Body curvature (\(\\rho_c\)) expressed as the ratio between the radius of an osculating circle and scatterer length. Larger values indicate straighter bodies. For instance, \(\\rho_c > 10\) approximates a straight cylinder, while smaller values indicate more pronounced curvature.
- taper_orderfloat
Parameter controlling end tapering sharpness.
- Returns:
- Tuple[numpy.ndarray[float], numpy.ndarray[float], numpy.ndarray[float], numpy.ndarray[float], numpy.ndarray[float]
- tapernumpy.ndarray[float]
Tapering factor along body axis with values bounded in [0,1] where 1 represents the full radius at the body center while 0 corresponds to pointed ends. radius at body center, 0 = pointed ends.
- gamma_tiltnumpy.ndarray[float]
Local tilt angles (radians) of curved body segments relative to he body coordinate system. Used for phase calculations.
- beta_tiltnumpy.ndarray[float]
Local orientation angles (radians) of segments relative to incident acoustic wave direction. Critical for scattering.
- r_posnumpy.ndarray[float]
Position vector along curved body axis in normalized coordinates using the Euclidean distances from body center to each segment.
- dr_posnumpy.ndarray[float]
Differential position vector containing incremental distances between adjacent segments.
Notes
The uniformly bent cylinder model represents scatterers with:
Uniform curvature: Constant radius of curvature along body axis
Symmetric tapering: Gradual radius reduction toward both ends
Smooth geometry: Continuous derivatives for stable numerics
Let \(z\) be the normalized longitudinal coordinates along the scatterer’s body axis, ranging from -1 (anterior) to 1 (posterior). The curvature is defined by the parameter \(\\gamma = 0.5 / \\rho_c\), where \(\\rho_c\) is the radius_of_curvature_ratio. The curvilinear coordinates that make up the position vector are given by:
\[\begin{split}x(z) = 1 - \\sqrt{1 - (\\sin(\\gamma z))^2}, \\quad z'(z) = \\sin(\\gamma z)\end{split}\]Simultaneously, the tapering function is defined as:
\[\begin{split}\\mathscr{T}(z) = \\sqrt{1 - z^{\\mathscr{t}}}\end{split}\]where \(\\mathscr{T}\) is the along-axis taper and \(\\mathscr{t}\) is the taper order. The local angles between segments (
gamma_tilt) and relative to the incident wave (beta_tilt) can have a large impact on scattering calculations. These angles determine the effective scattering cross-section and phase relationships in models such as the phase-compensated distorted wave Born approximation (PCDWBA).References
[1]Stanton, T.K. (1989). Sound scattering by cylinders of finite length. III. Deformed cylinders. The Journal of the Acoustical Society of America, 86, 691-705. doi: 10.1121/1.398193
Examples
>>> # Single moderately curved organism with 50 segments >>> taper, gamma_tilt, beta_tilt, r_pos, dr_pos = uniformly_bent_cylinder( ... n_segments=50, ... radius_of_curvature_ratio=3.0, # moderate curvature ... taper_order=10.0 # moderate tapering ... ) >>> >>> # Batch processing for multiple organisms >>> n_segments_array = np.array([30, 50, 40]) # different discretizations >>> results = uniformly_bent_cylinder( ... n_segments=n_segments_array, ... radius_of_curvature_ratio=4.0, ... taper_order=8.0 ... )