Two dimensional turbulence example

In this example, we initialize a random velocity field and observe its viscous, turbulent decay in a two-dimensional domain. This example demonstrates:

  • How to run a model with no buoyancy equation or tracers;
  • How to create user-defined fields
  • How to use differentiation functions

For this example, we need PyPlot for plotting and Statistics for setting up a random initial condition with zero mean velocity.

using Oceananigans, Oceananigans.AbstractOperations, PyPlot, Statistics

In addition to importing plotting and statistics packages, we import some types from Oceananigans that will aid in the calculation and visualization of voriticty.

using Oceananigans: Face, Cell

Face and Cell represent "locations" on the staggered grid. We instantiate the model with a simple isotropic diffusivity.

model = Model(
        grid = RegularCartesianGrid(size=(128, 128, 1), length=(2π, 2π, 2π)),
    buoyancy = nothing,
     tracers = nothing,
     closure = ConstantIsotropicDiffusivity(ν=1e-3, κ=1e-3)
)
Oceananigans.Model on a CPU architecture (time = 0.000 ns, iteration = 0) 
├── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
├── tracers: ()
├── closure: ConstantIsotropicDiffusivity{Float64,NamedTuple{(),Tuple{}}}
├── buoyancy: Nothing
├── coriolis: Nothing
├── output writers: OrderedCollections.OrderedDict{Symbol,Oceananigans.AbstractOutputWriter} with no entries
└── diagnostics: OrderedCollections.OrderedDict{Symbol,Oceananigans.AbstractDiagnostic} with no entries

Our initial condition randomizes u and v. We also ensure that both have zero mean for purely aesthetic reasons.

u₀ = rand(size(model.grid)...)
u₀ .-= mean(u₀)

set!(model, u=u₀, v=u₀)

Next we create an object called an Operation that represents a vorticity calculation. We'll use this object to calculate vorticity on-line as the simulation progresses.

u, v, w = model.velocities
(u = Field at (Oceananigans.Face, Oceananigans.Cell, Oceananigans.Cell)
├── data: OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
    ├── size: (128, 128, 1)
    └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
, v = Field at (Oceananigans.Cell, Oceananigans.Face, Oceananigans.Cell)
├── data: OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
    ├── size: (128, 128, 1)
    └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
, w = Field at (Oceananigans.Cell, Oceananigans.Cell, Oceananigans.Face)
├── data: OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
    ├── size: (128, 128, 1)
    └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
)

Create an object that represents the 'operation' required to compute vorticity.

vorticity_operation = ∂x(v) - ∂y(u)
BinaryOperation at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell)
├── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
│   ├── size: (128, 128, 1)
│   └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
└── tree: 

- at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
├── ∂xᶠᵃᵃ at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
│   └── OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── ∂yᵃᶠᵃ at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
    └── OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}

The instance vorticity_operation is a binary subtraction between two derivative operations acting on OffsetArrays (the underyling representation of u, and v). In order to use vorticity_operation we create a field ω to store the result of the operation, and a Computation object for coordinate the computation of vorticity and storage in ω:

ω = Field(Face, Face, Cell, model.architecture, model.grid)

vorticity_computation = Computation(vorticity_operation, ω)
Oceananigans.AbstractOperations.Computation{Field{Oceananigans.Face,Oceananigans.Face,Oceananigans.Cell,OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}},RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}},UnionAll,Oceananigans.AbstractOperations.BinaryOperation{Oceananigans.Face,Oceananigans.Face,Oceananigans.Cell,typeof(-),Oceananigans.AbstractOperations.Derivative{Oceananigans.Face,Oceananigans.Face,Oceananigans.Cell,typeof(∂xᶠᵃᵃ),OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}},typeof(identity),RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}},Oceananigans.AbstractOperations.Derivative{Oceananigans.Face,Oceananigans.Face,Oceananigans.Cell,typeof(∂yᵃᶠᵃ),OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}},typeof(identity),RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}},typeof(identity),typeof(identity),typeof(identity),RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}},RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}}(BinaryOperation at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell)
├── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
│   ├── size: (128, 128, 1)
│   └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
└── tree: 

- at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
├── ∂xᶠᵃᵃ at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
│   └── OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── ∂yᵃᶠᵃ at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell) via identity
    └── OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}, Field at (Oceananigans.Face, Oceananigans.Face, Oceananigans.Cell)
├── data: OffsetArrays.OffsetArray{Float64,3,Array{Float64,3}}
└── grid: RegularCartesianGrid{Float64,StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}}
    ├── size: (128, 128, 1)
    └── domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [-6.283185307179586, 0.0]
, RegularCartesianGrid{Float64}
domain: x ∈ [0.0, 6.283185307179586], y ∈ [0.0, 6.283185307179586], z ∈ [0.0, -6.283185307179586]
  resolution (Nx, Ny, Nz) = (128, 128, 1)
   halo size (Hx, Hy, Hz) = (1, 1, 1)
grid spacing (Δx, Δy, Δz) = (0.04908738521234052, 0.04908738521234052, 6.283185307179586), Array)

We ask for computation of vorticity by writing

compute!(vorticity_computation),

as shown below.

Finally, we run the model.

fig, ax = subplots()

for i = 1:10
    time_step!(model, Nt=100, Δt=1e-1)

    compute!(vorticity_computation)

    cla()
    imshow(interior(ω)[:, :, 1])
    ax.axis("off")
    pause(0.1)
end

We plot out the final vorticity field.

gcf()

This page was generated using Literate.jl.