PyTurbo_SF: Structure Functions Reference
This document provides the mathematical foundations and API reference for structure function computations in PyTurbo_SF.
Mathematical Foundations
Structure functions measure statistical properties of turbulent field increments across separation distances. The general form is:
where \(\phi\) represents the field variable, \(\vec{r}\) is the separation vector, and \(\langle \cdot \rangle_{\vec{x}}\) denotes spatial averaging.
1D Structure Functions
For 1D data (time series), PyTurbo_SF supports:
Scalar Structure Function (fun='scalar')
Scalar-Scalar Structure Function (fun='scalar_scalar')
Important
1D only supports scalar and scalar_scalar function types.
2D Structure Functions
For 2D velocity fields \(\vec{u} = (u, v)\):
Longitudinal Structure Function (fun='longitudinal')
Projects velocity increments onto the separation vector:
where \(\delta u_L = [\mathbf{u}(\mathbf{x} + \mathbf{r}) - \mathbf{u}(\mathbf{x})] \cdot \hat{\mathbf{r}}\)
Transverse Structure Function (fun='transverse')
Uses the perpendicular velocity component:
where \(\delta u_T = |[\mathbf{u}(\mathbf{x} + \mathbf{r}) - \mathbf{u}(\mathbf{x})] \times \hat{\mathbf{r}}|\)
Default Velocity Structure Function (fun='default_vel')
Sum of individual velocity component structure functions:
Advective Structure Function (fun='advective')
Third-order structure function including stress tensor components for energy flux:
Scalar Structure Function (fun='scalar')
Scalar-Scalar Structure Function (fun='scalar_scalar')
Longitudinal-Transverse (fun='longitudinal_transverse')
Longitudinal-Scalar (fun='longitudinal_scalar')
Transverse-Scalar (fun='transverse_scalar')
3D Structure Functions
For 3D velocity fields \(\vec{u} = (u, v, w)\):
Longitudinal (fun='longitudinal')
Requires all three velocity components ['u', 'v', 'w'].
Default Velocity (fun='default_vel')
Sum of all three velocity component structure functions:
Transverse (plane-specific)
fun='transverse_ij'— xy-plane, requires['u', 'v']fun='transverse_ik'— xz-plane, requires['u', 'w']fun='transverse_jk'— yz-plane, requires['v', 'w']
Longitudinal-Transverse (plane-specific)
fun='longitudinal_transverse_ij'fun='longitudinal_transverse_ik'fun='longitudinal_transverse_jk'
Transverse-Scalar (plane-specific)
fun='transverse_ij_scalar'fun='transverse_ik_scalar'fun='transverse_jk_scalar'
Scalar types: scalar, scalar_scalar, longitudinal_scalar
Bessel Energy Flux Decomposition
Warning
Energy flux decomposition is only available in 2D via get_energy_flux_2d.
Only fun='advective' and fun='scalar_scalar' are supported.
Core Formula
The spectral energy flux \(\Pi(K)\) is computed from the angle-averaged advective structure function using a Bessel \(J_1\) transform:
where:
\(\Pi(K)\) — Spectral energy flux at wavenumber \(K\)
\(\tilde{D}_{LLL}(r)\) — Angle-averaged advective structure function
\(J_1(Kr)\) — Bessel function of the first kind, order 1
\(K\) — Wavenumber magnitude
Physical Interpretation
Sign of \(\Pi\) |
Physical Meaning |
|---|---|
\(\Pi(K) > 0\) |
Forward cascade: energy flows to smaller scales |
\(\Pi(K) < 0\) |
Inverse cascade: energy flows to larger scales |
The magnitude \(|\Pi(K)|\) represents the rate of energy transfer across wavenumber \(K\).
Derivation
The formula derives from the Plancherel theorem relating spectral energy flux to real-space structure functions. For homogeneous isotropic turbulence:
Using the identity \(\frac{d}{dz}[z J_0(z)] = z J_1(z)\) and integration by parts yields the working formula.
Numerical Implementation
The integral is discretized over radial bins:
Conditional Structure Functions
Conditional structure functions compute statistics conditioned on a secondary field, enabling scale-dependent analysis of turbulence-scalar interactions.
Mathematical Definition
where:
\(C(\mathbf{x})\) — Conditioning variable (e.g., vorticity, temperature, strain rate)
\([C_{\text{lo}}, C_{\text{hi}})\) — Conditioning bin range (half-open interval)
Indicator Function Formulation
Using indicator functions:
where:
Implementation
Single Conditioning Bin:
conditioning_bins = [C_lo, C_hi]
Multiple Conditioning Bins:
conditioning_bins = np.linspace(C_min, C_max, N+1) # N bins
# or
conditioning_bins = np.logspace(log10(C_min), log10(C_max), N+1)
Note
When using multiple bins, the output dataset includes an additional cond_bin dimension
with coordinates at bin centers.
Typical Conditioning Variables
Conditioning Variable |
Application |
|---|---|
Vorticity magnitude |
Separate rotational from irrotational regions |
Temperature |
Stratification effects on turbulence |
Strain rate |
Strain-dominated vs. rotation-dominated regions |
Scalar gradient |
Frontal vs. well-mixed regions |
Turbulent kinetic energy |
High-energy vs. low-energy turbulence regions |
Function Reference
Important
1D supports only: scalar and scalar_scalar
Energy flux (Bessel) supports only: advective and scalar_scalar (2D only)
1D and 2D Function Types
|
|
1D |
2D bin |
2D iso |
2D flux |
|---|---|---|---|---|---|
|
|
✓ |
✓ |
✓ |
|
|
|
✓ |
✓ |
✓ |
✓ |
|
|
✓ |
✓ |
||
|
|
✓ |
✓ |
||
|
|
✓ |
✓ |
||
|
|
✓ |
✓ |
✓ |
|
|
|
✓ |
✓ |
||
|
|
✓ |
✓ |
||
|
|
✓ |
✓ |
3D Function Types
|
|
3D bin |
3D iso |
|---|---|---|---|
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
|
|
✓ |
✓ |
Note
3D Energy Flux (get_energy_flux_3d) is not implemented.
Methods Summary
Method |
Dim |
Description |
Output Coord |
|---|---|---|---|
|
1D |
Binned SF for time series (scalar only) |
|
|
2D |
Binned SF in Cartesian coordinates |
|
|
3D |
Binned SF in Cartesian coordinates |
|
|
2D |
Radially-averaged SF with isotropy diagnostics |
|
|
3D |
Spherically-averaged SF with isotropy diagnostics |
|
|
2D |
Bessel J1 energy flux (advective/scalar_scalar) |
|
API Reference
bin_sf_1d
bin_sf_1d(
ds, # xarray.Dataset
variables_names, # ['scalar'] or ['phi', 'psi']
order, # 2 or (1, 1)
bins, # {'time': time_edges}
bootsize=None,
fun='scalar', # 'scalar' or 'scalar_scalar' ONLY
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
confidence_interval=0.95,
seed=42
)
bin_sf_2d
bin_sf_2d(
ds, # xarray.Dataset with 2D fields
variables_names, # See function reference table
order, # 2, 3, or (1, 1)
bins, # {'x': x_edges, 'y': y_edges}
bootsize=None,
fun='longitudinal',
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
seed=42
)
bin_sf_3d
bin_sf_3d(
ds, # xarray.Dataset with 3D fields
variables_names, # See function reference table
order,
bins, # {'x': x_edges, 'y': y_edges, 'z': z_edges}
bootsize=None,
fun='longitudinal',
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
seed=42
)
get_isotropic_sf_2d
get_isotropic_sf_2d(
ds,
variables_names,
order=2.0,
bins=None, # {'r': r_edges}
bootsize=None,
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
fun='longitudinal',
n_bins_theta=36,
window_size_theta=None,
window_size_r=None,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
confidence_interval=0.95,
seed=42
)
get_isotropic_sf_3d
get_isotropic_sf_3d(
ds,
variables_names,
order=2.0,
bins=None, # {'r': r_edges}
bootsize=None,
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
fun='longitudinal',
n_bins_theta=36,
n_bins_phi=18,
window_size_theta=None,
window_size_phi=None,
window_size_r=None,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
confidence_interval=0.95,
seed=42
)
get_energy_flux_2d
get_energy_flux_2d(
ds,
variables_names, # ['u', 'v', 'tau1', 'tau2'] or ['u', 'v', 'w', 'tau1', 'tau2', 'tau3']
order=3.0,
wavenumbers=None,
r_bins=None,
bootsize=None,
initial_nbootstrap=100,
max_nbootstrap=1000,
step_nbootstrap=100,
fun='advective', # 'advective' or 'scalar_scalar' ONLY
n_bins_theta=36,
n_r_bins=100,
window_size_theta=None,
window_size_k=None,
convergence_eps=0.1,
n_jobs=-1,
backend='threading',
conditioning_var=None,
conditioning_bins=None,
confidence_interval=0.95,
seed=42
)
Common Parameters
Parameter |
Description |
|---|---|
|
|
|
List of variable names (see function reference tables) |
|
Structure function order: |
|
Dict of bin edges: |
|
Bootstrap block size; should be |
|
Structure function type (see tables above) |
|
Initial bootstrap iterations (default: 100) |
|
Maximum bootstrap iterations (default: 1000) |
|
Convergence threshold for standard error (default: 0.1) |
|
Parallel jobs: |
|
|
|
Name of conditioning variable in dataset |
|
|
|
Confidence level (default: 0.95) |
|
Integer to ensure reproducibility at the initial phase of the bootstrapping |
Output Variables
All output datasets include:
Variable |
Description |
|---|---|
|
Mean structure function or energy flux |
|
Standard error from bootstrap |
|
Confidence interval bounds |
|
Number of point pairs per bin |
|
Boolean convergence status |
|
Bootstrap iterations used per bin |
|
Isotropy error (isotropic methods only) |
|
Homogeneity error (isotropic methods only) |
|
Combined quality mask |
Usage Examples
2D Longitudinal SF (Isotropic)
import numpy as np
from pyturbo_sf import get_isotropic_sf_2d
ds_sf = get_isotropic_sf_2d(
ds,
variables_names=['u', 'v'],
order=2.0,
bins={'r': np.logspace(2, 4, 30)},
fun='longitudinal',
bootsize={'y': 64, 'x': 64}
)
1D Scalar SF
from pyturbo_sf import bin_sf_1d
ds_sf = bin_sf_1d(
ds,
variables_names=['temperature'],
order=2,
bins={'time': np.linspace(0, 3600, 50)},
fun='scalar'
)
Energy Flux (2D)
from pyturbo_sf import get_energy_flux_2d
ds_flux = get_energy_flux_2d(
ds,
variables_names=['u', 'v', 'tau1', 'tau2'],
order=3.0,
fun='advective',
bootsize={'y': 64, 'x': 64}
)
Conditional SF (Single Bin)
ds_sf = get_isotropic_sf_2d(
ds,
variables_names=['u', 'v'],
fun='longitudinal',
conditioning_var='vorticity',
conditioning_bins=[0.001, 0.01]
)
Conditional SF (Multiple Bins)
import numpy as np
ds_sf = get_isotropic_sf_2d(
ds,
variables_names=['u', 'v'],
fun='longitudinal',
conditioning_var='vorticity',
conditioning_bins=np.logspace(-4, -1, 6) # 5 bins
)
# Access specific conditioning bin
ds_sf.sel(cond_bin=0.001, method='nearest')
3D Spherically-Averaged SF
from pyturbo_sf import get_isotropic_sf_3d
ds_sf = get_isotropic_sf_3d(
ds,
variables_names=['u', 'v', 'w'],
order=2.0,
bins={'r': np.logspace(0, 3, 25)},
fun='longitudinal',
bootsize={'z': 32, 'y': 32, 'x': 32}
)
References
Kolmogorov, A. N. (1941). The local structure of turbulence in incompressible viscous fluid for very large Reynolds numbers. Dokl. Akad. Nauk SSSR, 30, 301-305.
Lindborg, E. (1999). Can the atmospheric kinetic energy spectrum be explained by two-dimensional turbulence? J. Fluid Mech., 388, 259-288.
Cho, J. Y. N., & Lindborg, E. (2001). Horizontal velocity structure functions in the upper troposphere and lower stratosphere. J. Geophys. Res., 106(D10), 10223-10232.