Kinetic energy equation

The KineticEnergyEquation module provides diagnostics for every term in the resolved kinetic energy (KE) budget. The kinetic energy per unit mass is defined as

\[K = \tfrac{1}{2} u_i u_i\]

and its prognostic equation is obtained by contracting the momentum equation with the velocity:

\[\partial_t K = \underbrace{-u_i \partial_j(u_i u_j)}_{\text{advection}} + \underbrace{u_i \partial_j \tau_{ij}}_{\text{stress}} - \underbrace{u_i \partial_i p}_{\text{pressure}} + \underbrace{u_i b_i}_{\text{buoyancy}} + \underbrace{u_i F_{u_i}}_{\text{forcing}}\]

where $\tau_{ij}$ is the viscous/subgrid stress tensor, $p$ is pressure, $b_i$ is the buoyancy acceleration component in the $i$-th direction, and $F_{u_i}$ is the forcing on the $i$-th momentum equation.

This decomposition is essential for understanding how kinetic energy is generated (e.g. by buoyancy production or forcing), redistributed (by advection or pressure work), and removed (by viscous dissipation). The module also provides two formulations of the dissipation rate: a general one based on the full stress tensor ($\varepsilon = \partial_j u_i \cdot \tau_{ij}$), and an isotropic version ($\varepsilon = 2\nu S_{ij} S_{ij}$) valid when the turbulence closure uses a single scalar viscosity.

All diagnostics are computed at (Center, Center, Center).

Example

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid; buoyancy=BuoyancyTracer(), tracers=:b, closure=ScalarDiffusivity=1e-4));julia> KE = KineticEnergyEquation.KineticEnergy(model)KineticEnergy KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: kinetic_energy_ccc (generic function with 1 method)└── arguments: ("Field", "Field", "Field")└── computes: kinetic energy  ½uᵢuᵢjulia> ε = KineticEnergyEquation.KineticEnergyIsotropicDissipationRate(model)KineticEnergyIsotropicDissipationRate KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: isotropic_viscous_dissipation_rate_ccc (generic function with 1 method)└── arguments: ("Field", "Field", "Field", "NamedTuple")└── computes: isotropic kinetic energy dissipation rate  ε = 2νSᵢⱼSᵢⱼjulia> wb = KineticEnergyEquation.PotentialEnergyConversion(model)PotentialToKineticEnergyConversion KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: uᵢbᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "BuoyancyForce", "NamedTuple")└── computes: potential to kinetic energy conversion  uᵢbᵢ

Kinetic energy

Oceanostics.KineticEnergyEquation.KineticEnergyType
KineticEnergy(model; kwargs...)

Calculate the kinetic energy of model.

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid);julia> KE = KineticEnergyEquation.KineticEnergy(model)KineticEnergy KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: kinetic_energy_ccc (generic function with 1 method)└── arguments: ("Field", "Field", "Field")└── computes: kinetic energy  ½uᵢuᵢ
source

Tendency

Oceanostics.KineticEnergyEquation.KineticEnergyTendencyType
KineticEnergyTendency(model; location)

Return a KernelFunctionOperation that computes the tendency uᵢGᵢ of the KE, excluding the nonhydrostatic pressure contribution:

KET = ½∂ₜuᵢ² = uᵢGᵢ - uᵢ∂ᵢpₙₕₛ
julia> using Oceananigansjulia> grid = RectilinearGrid(size = (1, 1, 4), extent = (1, 1, 1));julia> model = NonhydrostaticModel(grid);julia> using Oceanostics.KineticEnergyEquation: KineticEnergyTendencyjulia> ke_tendency = KineticEnergyTendency(model)KineticEnergyTendency KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢGᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("Centered", "Nothing", "Nothing", "Nothing", "Nothing", "Nothing", "Nothing", "Nothing", "Oceananigans.Models.NonhydrostaticModels.BackgroundFields", "NamedTuple", "NamedTuple", "NamedTuple", "Nothing", "Nothing", "Clock", "NamedTuple")└── computes: kinetic energy tendency  uᵢGᵢ (excl. nonhydrostatic pressure)
source

Advection

Oceanostics.KineticEnergyEquation.KineticEnergyAdvectionType
KineticEnergyAdvection(model; velocities, location)

Return a KernelFunctionOperation that computes the advection term, defined as

ADV = uᵢ∂ⱼ(uᵢuⱼ)

By default, the buoyancy production will be calculated using the resolved velocities and users cab use the keyword velocities to modify that behavior:

julia> using Oceananigansjulia> grid = RectilinearGrid(size = (1, 1, 4), extent = (1,1,1));julia> model = NonhydrostaticModel(grid);julia> using Oceanostics.KineticEnergyEquation: KineticEnergyAdvectionjulia> ADV = KineticEnergyAdvection(model)KineticEnergyAdvection KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢ∂ⱼuⱼuᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "Centered")└── computes: kinetic energy advection  uᵢ∂ⱼ(uᵢuⱼ)
source

Stress (diffusive) term

Oceanostics.KineticEnergyEquation.KineticEnergyStressType
KineticEnergyStress(model; location)

Return a KernelFunctionOperation that computes the diffusive term of the KE prognostic equation:

    DIFF = uᵢ∂ⱼτᵢⱼ

where uᵢ are the velocity components and τᵢⱼ is the diffusive flux of i momentum in the j-th direction.

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid; closure=ScalarDiffusivity=1e-4));julia> DIFF = KineticEnergyEquation.KineticEnergyStress(model)KineticEnergyStress KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: uᵢ∂ⱼ_τᵢⱼᶜᶜᶜ (generic function with 1 method)└── arguments: ("ScalarDiffusivity", "Nothing", "Clock", "NamedTuple", "Nothing")└── computes: kinetic energy stress/diffusion  uᵢ∂ⱼτᵢⱼ
source

Forcing

Oceanostics.KineticEnergyEquation.KineticEnergyForcingType
KineticEnergyForcing(model; location)

Return a KernelFunctionOperation that computes the forcing term of the KE prognostic equation:

    FORC = uᵢFᵤᵢ

where uᵢ are the velocity components and Fᵤᵢ is the forcing term(s) in the uᵢ prognostic equation (i.e. the forcing for uᵢ).

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid);julia> FORC = KineticEnergyEquation.KineticEnergyForcing(model)KineticEnergyForcing KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: uᵢFᵤᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "Clock", "NamedTuple")└── computes: kinetic energy forcing  uᵢFᵤᵢ
source

Pressure redistribution

Oceanostics.KineticEnergyEquation.KineticEnergyPressureRedistributionType
KineticEnergyPressureRedistribution(
    model;
    velocities,
    pressure,
    location
)

Return a KernelFunctionOperation that computes the pressure redistribution term:

PR = uᵢ∂ᵢp

where p is the pressure. By default p is taken to be the total pressure (nonhydrostatic + hydrostatic):

julia> using Oceananigansjulia> grid = RectilinearGrid(size = (1, 1, 4), extent = (1,1,1));julia> model = NonhydrostaticModel(grid);julia> using Oceanostics.KineticEnergyEquation: KineticEnergyPressureRedistributionjulia> ∇u⃗p = KineticEnergyPressureRedistribution(model)KineticEnergyPressureRedistribution KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢ∂ᵢpᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "Field")└── computes: kinetic energy pressure redistribution  uᵢ∂ᵢp

We can also pass velocities and pressure keywords to perform more specific calculations. The example below illustrates calculation of the nonhydrostatic contribution to the pressure redistrubution term:

julia> ∇u⃗pNHS = KineticEnergyPressureRedistribution(model, pressure=model.pressures.pNHS)KineticEnergyPressureRedistribution KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢ∂ᵢpᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "Field")└── computes: kinetic energy pressure redistribution  uᵢ∂ᵢp
source

Potential energy conversion

Oceanostics.KineticEnergyEquation.PotentialEnergyConversionType
PotentialEnergyConversion(
    model;
    velocities,
    tracers,
    location
)

Return a KernelFunctionOperation that computes the conversion of potential energy into kinetic energy, defined as

uᵢbᵢ

where bᵢ is the component of the buoyancy acceleration in the i-th direction (which is zero for x and y, except when gravity_unit_vector isn't aligned with the grid's z-direction) and all three components of i=1,2,3 are added up.

By default, the conversion will be calculated using the resolved velocities and tracers:

julia> using Oceananigansjulia> grid = RectilinearGrid(size = (1, 1, 4), extent = (1,1,1));julia> model = NonhydrostaticModel(grid; buoyancy=BuoyancyTracer(), tracers=:b);julia> using Oceanostics.KineticEnergyEquation: PotentialEnergyConversionjulia> wb = PotentialEnergyConversion(model)PotentialToKineticEnergyConversion KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢbᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "BuoyancyForce", "NamedTuple")└── computes: potential to kinetic energy conversion  uᵢbᵢ

If we want to calculate only the turbulent conversion rate, we can do so by passing turbulent perturbations to the velocities and/or tracers options):

julia> w′ = Field(model.velocities.w - Field(Average(model.velocities.w)));julia> b′ = Field(model.tracers.b - Field(Average(model.tracers.b)));julia> w′b′ = PotentialEnergyConversion(model, velocities=(u=model.velocities.u, v=model.velocities.v, w=w′), tracers=(b=b′,))PotentialToKineticEnergyConversion KernelFunctionOperation at (Center, Center, Center)├── grid: 1×1×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 1×1×3 halo├── kernel_function: uᵢbᵢᶜᶜᶜ (generic function with 1 method)└── arguments: ("NamedTuple", "BuoyancyForce", "NamedTuple")└── computes: potential to kinetic energy conversion  uᵢbᵢ
source

Dissipation rate

Oceanostics.KineticEnergyEquation.DissipationRateType
DissipationRate(model; U, V, W, location)

Calculate the Kinetic Energy Dissipation Rate, defined as

ε = ∂ⱼuᵢ  τᵢⱼ

where ∂ⱼuᵢ is the velocity gradient tensor and τᵢⱼ is the stress tensor.

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid; closure=ScalarDiffusivity=1e-4));julia> ε = KineticEnergyEquation.DissipationRate(model)KineticEnergyDissipationRate KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: viscous_dissipation_rate_ccc (generic function with 1 method)└── arguments: ("Nothing", "NamedTuple", "NamedTuple")└── computes: kinetic energy dissipation rate  ε = ∂ⱼuᵢ·τᵢⱼ
source
Oceanostics.KineticEnergyEquation.KineticEnergyIsotropicDissipationRateType
KineticEnergyIsotropicDissipationRate(
    u,
    v,
    w,
    closure,
    closure_fields,
    model_fields,
    clock;
    location
)

Calculate the Viscous Dissipation Rate as

ε = 2 ν SᵢⱼSᵢⱼ,

where Sᵢⱼ is the strain rate tensor, for a fluid with an isotropic turbulence closure (i.e., a turbulence closure where ν (eddy or not) is the same for all directions).

julia> using Oceananigans, Oceanosticsjulia> grid = RectilinearGrid(size=(4, 4, 4), extent=(1, 1, 1));julia> model = NonhydrostaticModel(grid; closure=ScalarDiffusivity=1e-4));julia> ε = KineticEnergyEquation.KineticEnergyIsotropicDissipationRate(model)KineticEnergyIsotropicDissipationRate KernelFunctionOperation at (Center, Center, Center)├── grid: 4×4×4 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo├── kernel_function: isotropic_viscous_dissipation_rate_ccc (generic function with 1 method)└── arguments: ("Field", "Field", "Field", "NamedTuple")└── computes: isotropic kinetic energy dissipation rate  ε = 2νSᵢⱼSᵢⱼ
source