Wind- and convection-driven mixing in an ocean surface boundary layer

This example simulates mixing by three-dimensional turbulence in an ocean surface boundary layer driven by atmospheric winds and convection. It demonstrates:

  • How to set-up a grid with varying spacing in the vertical direction
  • How to use the SeawaterBuoyancy model for buoyancy with TEOS10EquationOfState.
  • How to use a turbulence closure for large eddy simulation.
  • How to use a function to impose a boundary condition.

Install dependencies

First let's make sure we have all required packages installed.

using Pkg
pkg"add Oceananigans, CairoMakie, SeawaterPolynomials, CUDA"

We start by importing all of the packages and functions that we'll need for this example.

using Oceananigans
using Oceananigans.Units

using CUDA
using Random
using Printf
using CairoMakie
using SeawaterPolynomials.TEOS10: TEOS10EquationOfState

The grid

We use 128²×64 grid points with 1 m grid spacing in the horizontal and varying spacing in the vertical, with higher resolution closer to the surface. Here we use a stretching function for the vertical nodes that maintains relatively constant vertical spacing in the mixed layer, which is desirable from a numerical standpoint:

Nx = Ny = 128    # number of points in each of horizontal directions
Nz = 64          # number of points in the vertical direction

Lx = Ly = 128    # (m) domain horizontal extents
Lz = 64          # (m) domain depth

refinement = 1.2 # controls spacing near surface (higher means finer spaced)
stretching = 12  # controls rate of stretching at bottom

# Normalized height ranging from 0 to 1
h(k) = (k - 1) / Nz

# Linear near-surface generator
ζ₀(k) = 1 + (h(k) - 1) / refinement

# Bottom-intensified stretching function
Σ(k) = (1 - exp(-stretching * h(k))) / (1 - exp(-stretching))

# Generating function
z_interfaces(k) = Lz * (ζ₀(k) * Σ(k) - 1)

grid = RectilinearGrid(GPU(),
                       size = (Nx, Nx, Nz),
                       x = (0, Lx),
                       y = (0, Ly),
                       z = z_interfaces)
128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CUDAGPU with 3×3×3 halo
├── Periodic x ∈ [0.0, 128.0) regularly spaced with Δx=1.0
├── Periodic y ∈ [0.0, 128.0) regularly spaced with Δy=1.0
└── Bounded  z ∈ [-64.0, 0.0] variably spaced with min(Δz)=0.833413, max(Δz)=1.96618

We plot vertical spacing versus depth to inspect the prescribed grid stretching:

fig = Figure(size=(1200, 800))
ax = Axis(fig[1, 1], ylabel = "z (m)", xlabel = "Vertical spacing (m)")

lines!(ax, zspacings(grid, Center()))
scatter!(ax, zspacings(grid, Center()))

fig

Buoyancy that depends on temperature and salinity

We use the SeawaterBuoyancy model with the TEOS10 equation of state,

ρₒ = 1026 # kg m⁻³, average density at the surface of the world ocean
equation_of_state = TEOS10EquationOfState(reference_density=ρₒ)
buoyancy = SeawaterBuoyancy(; equation_of_state)
SeawaterBuoyancy{Float64}:
├── gravitational_acceleration: 9.80665
└── equation_of_state: BoussinesqEquationOfState{Float64}

Boundary conditions

We calculate the surface temperature flux associated with surface cooling of 200 W m⁻², reference density ρₒ, and heat capacity cᴾ,

Q = 200   # W m⁻², surface _heat_ flux
cᴾ = 3991 # J K⁻¹ kg⁻¹, typical heat capacity for seawater

Jᵀ = Q / (ρₒ * cᴾ) # K m s⁻¹, surface _temperature_ flux
4.884283985946938e-5

Finally, we impose a temperature gradient dTdz both initially (see "Initial conditions" section below) and at the bottom of the domain, culminating in the boundary conditions on temperature,

dTdz = 0.01 # K m⁻¹

T_bcs = FieldBoundaryConditions(top = FluxBoundaryCondition(Jᵀ),
                                bottom = GradientBoundaryCondition(dTdz))
Oceananigans.FieldBoundaryConditions, with boundary conditions
├── west: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── east: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── south: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── north: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── bottom: GradientBoundaryCondition: 0.01
├── top: FluxBoundaryCondition: 4.88428e-5
└── immersed: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)

Note that a positive temperature flux at the surface of the ocean implies cooling. This is because a positive temperature flux implies that temperature is fluxed upwards, out of the ocean.

For the velocity field, we imagine a wind blowing over the ocean surface with an average velocity at 10 meters u₁₀, and use a drag coefficient cᴰ to estimate the kinematic stress (that is, stress divided by density) exerted by the wind on the ocean:

u₁₀ = 10  # m s⁻¹, average wind velocity 10 meters above the ocean
cᴰ = 2e-3 # dimensionless drag coefficient
ρₐ = 1.2  # kg m⁻³, approximate average density of air at sea-level
τx = - ρₐ / ρₒ * cᴰ * u₁₀ * abs(u₁₀) # m² s⁻²
-0.00023391812865497074

The boundary conditions on u are thus

u_bcs = FieldBoundaryConditions(top = FluxBoundaryCondition(τx))
Oceananigans.FieldBoundaryConditions, with boundary conditions
├── west: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── east: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── south: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── north: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── bottom: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── top: FluxBoundaryCondition: -0.000233918
└── immersed: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)

For salinity, S, we impose an evaporative flux of the form

@inline Jˢ(x, y, t, S, evaporation_rate) = - evaporation_rate * S # [salinity unit] m s⁻¹

where S is salinity. We use an evaporation rate of 1 millimeter per hour,

evaporation_rate = 1e-3 / hour # m s⁻¹
2.7777777777777776e-7

We build the Flux evaporation BoundaryCondition with the function , indicating that depends on salinity S and passing the parameter evaporation_rate,

evaporation_bc = FluxBoundaryCondition(Jˢ, field_dependencies=:S, parameters=evaporation_rate)
FluxBoundaryCondition: ContinuousBoundaryFunction Jˢ at (Nothing, Nothing, Nothing)

The full salinity boundary conditions are

S_bcs = FieldBoundaryConditions(top=evaporation_bc)
Oceananigans.FieldBoundaryConditions, with boundary conditions
├── west: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── east: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── south: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── north: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── bottom: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)
├── top: FluxBoundaryCondition: ContinuousBoundaryFunction Jˢ at (Nothing, Nothing, Nothing)
└── immersed: DefaultBoundaryCondition (FluxBoundaryCondition: Nothing)

Model instantiation

We fill in the final details of the model here, i.e., Coriolis forces, and use the (scale-invariant) DynamicSmagorinsky closure for large eddy simulation to model the effect of turbulent motions at scales smaller than the grid scale that are not explicitly resolved.

model = NonhydrostaticModel(grid; buoyancy,
                            tracers = (:T, :S),
                            coriolis = FPlane(f=1e-4),
                            closure = DynamicSmagorinsky(),
                            boundary_conditions = (u=u_bcs, T=T_bcs, S=S_bcs))
NonhydrostaticModel{CUDAGPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
├── grid: 128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CUDAGPU with 3×3×3 halo
├── timestepper: RungeKutta3TimeStepper
├── advection scheme: Centered(order=2)
├── tracers: (T, S)
├── closure: Smagorinsky with coefficient = DynamicCoefficient(averaging = LagrangianAveraging(), schedule = IterationInterval(1, 0)), Pr=(T = 1.0, S = 1.0)
├── buoyancy: SeawaterBuoyancy with g=9.80665 and BoussinesqEquationOfState{Float64} with ĝ = NegativeZDirection()
└── coriolis: FPlane{Float64}(f=0.0001)

Note: To use the (constant-coefficient) Smagorinsky-Lilly turbulence closure rather than DynamicSmagorinsky, use closure = SmagorinskyLilly() in the model constructor.

Initial conditions

Our initial condition for temperature consists of a linear stratification superposed with random noise damped at the walls, while our initial condition for velocity consists only of random noise.

# Random noise damped at top and bottom
Ξ(z) = randn() * z / model.grid.Lz * (1 + z / model.grid.Lz) # noise

# Temperature initial condition: a stable density gradient with random noise superposed.
Tᵢ(x, y, z) = 20 + dTdz * z + dTdz * model.grid.Lz * 1e-6 * Ξ(z)

# Velocity initial condition: random noise scaled by the friction velocity.
uᵢ(x, y, z) = sqrt(abs(τx)) * 1e-3 * Ξ(z)

# `set!` the `model` fields using functions or constants:
set!(model, u=uᵢ, w=uᵢ, T=Tᵢ, S=35)

Setting up a simulation

We set-up a simulation with an initial time-step of 10 seconds that stops at 2 hours, with adaptive time-stepping and progress printing.

simulation = Simulation(model, Δt=10, stop_time=2hours)
Simulation of NonhydrostaticModel{CUDAGPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
├── Next time step: 10 seconds
├── run_wall_time: 0 seconds
├── run_wall_time / iteration: NaN days
├── stop_time: 2 hours
├── stop_iteration: Inf
├── wall_time_limit: Inf
├── minimum_relative_step: 0.0
├── callbacks: OrderedDict with 4 entries:
│   ├── stop_time_exceeded => Callback of stop_time_exceeded on IterationInterval(1)
│   ├── stop_iteration_exceeded => Callback of stop_iteration_exceeded on IterationInterval(1)
│   ├── wall_time_limit_exceeded => Callback of wall_time_limit_exceeded on IterationInterval(1)
│   └── nan_checker => Callback of NaNChecker for u on IterationInterval(100)
└── output_writers: OrderedDict with no entries

The TimeStepWizard helps ensure stable time-stepping with a Courant-Freidrichs-Lewy (CFL) number of 0.7.

conjure_time_step_wizard!(simulation, cfl=0.7)

Nice progress messaging is helpful:

# Print a progress message
progress_message(sim) = @printf("Iteration: %04d, time: %s, Δt: %s, max(|w|) = %.1e ms⁻¹, wall time: %s\n",
                                iteration(sim), prettytime(sim), prettytime(sim.Δt),
                                maximum(abs, sim.model.velocities.w), prettytime(sim.run_wall_time))

add_callback!(simulation, progress_message, IterationInterval(40))

We then set up the simulation:

Output

We use the JLD2Writer to save $x, z$ slices of the velocity fields, tracer fields, and eddy diffusivities. The prefix keyword argument to JLD2Writer indicates that output will be saved in ocean_wind_mixing_and_convection.jld2.

# Create a NamedTuple with eddy viscosity
eddy_viscosity = (; νₑ = model.closure_fields.νₑ)

filename = "ocean_wind_mixing_and_convection"

simulation.output_writers[:slices] =
    JLD2Writer(model, merge(model.velocities, model.tracers, eddy_viscosity),
               filename = filename * ".jld2",
               indices = (:, grid.Ny/2, :),
               schedule = TimeInterval(1minute),
               overwrite_existing = true)
JLD2Writer scheduled on TimeInterval(1 minute):
├── filepath: ocean_wind_mixing_and_convection.jld2
├── 6 outputs: (u, v, w, T, S, νₑ)
├── array_type: Array{Float32}
├── including: [:grid, :coriolis, :buoyancy, :closure]
├── file_splitting: NoFileSplitting
└── file size: 0 bytes (file not yet created)

We're ready:

run!(simulation)
[ Info: Initializing simulation...
Iteration: 0000, time: 0 seconds, Δt: 11 seconds, max(|w|) = 1.3e-05 ms⁻¹, wall time: 0 seconds
[ Info:     ... simulation initialization complete (7.799 seconds)
[ Info: Executing initial time step...
[ Info:     ... initial time step complete (8.574 seconds).
Iteration: 0040, time: 6.394 minutes, Δt: 6.380 seconds, max(|w|) = 6.4e-06 ms⁻¹, wall time: 19.699 seconds
Iteration: 0080, time: 9.825 minutes, Δt: 4.112 seconds, max(|w|) = 9.7e-06 ms⁻¹, wall time: 22.744 seconds
Iteration: 0120, time: 12.230 minutes, Δt: 3.281 seconds, max(|w|) = 1.1e-05 ms⁻¹, wall time: 25.733 seconds
Iteration: 0160, time: 14.242 minutes, Δt: 2.802 seconds, max(|w|) = 9.9e-06 ms⁻¹, wall time: 28.763 seconds
Iteration: 0200, time: 15.999 minutes, Δt: 2.482 seconds, max(|w|) = 8.0e-06 ms⁻¹, wall time: 31.779 seconds
Iteration: 0240, time: 17.542 minutes, Δt: 2.255 seconds, max(|w|) = 1.4e-05 ms⁻¹, wall time: 34.741 seconds
Iteration: 0280, time: 18.971 minutes, Δt: 2.077 seconds, max(|w|) = 2.8e-05 ms⁻¹, wall time: 37.695 seconds
Iteration: 0320, time: 20.295 minutes, Δt: 1.934 seconds, max(|w|) = 5.9e-05 ms⁻¹, wall time: 40.672 seconds
Iteration: 0360, time: 21.526 minutes, Δt: 1.816 seconds, max(|w|) = 1.1e-04 ms⁻¹, wall time: 43.744 seconds
Iteration: 0400, time: 22.703 minutes, Δt: 1.715 seconds, max(|w|) = 2.5e-04 ms⁻¹, wall time: 46.755 seconds
Iteration: 0440, time: 23.807 minutes, Δt: 1.626 seconds, max(|w|) = 5.0e-04 ms⁻¹, wall time: 49.786 seconds
Iteration: 0480, time: 24.847 minutes, Δt: 1.547 seconds, max(|w|) = 1.0e-03 ms⁻¹, wall time: 52.759 seconds
Iteration: 0520, time: 25.858 minutes, Δt: 1.472 seconds, max(|w|) = 2.1e-03 ms⁻¹, wall time: 55.889 seconds
Iteration: 0560, time: 26.815 minutes, Δt: 1.394 seconds, max(|w|) = 4.4e-03 ms⁻¹, wall time: 59.038 seconds
Iteration: 0600, time: 27.724 minutes, Δt: 1.317 seconds, max(|w|) = 8.3e-03 ms⁻¹, wall time: 1.034 minutes
Iteration: 0640, time: 28.574 minutes, Δt: 1.240 seconds, max(|w|) = 1.4e-02 ms⁻¹, wall time: 1.085 minutes
Iteration: 0680, time: 29.376 minutes, Δt: 1.153 seconds, max(|w|) = 2.3e-02 ms⁻¹, wall time: 1.136 minutes
Iteration: 0720, time: 30.108 minutes, Δt: 1.051 seconds, max(|w|) = 3.1e-02 ms⁻¹, wall time: 1.188 minutes
Iteration: 0760, time: 30.782 minutes, Δt: 950.471 ms, max(|w|) = 3.5e-02 ms⁻¹, wall time: 1.238 minutes
Iteration: 0800, time: 31.393 minutes, Δt: 853.196 ms, max(|w|) = 1.2e-01 ms⁻¹, wall time: 1.288 minutes
Iteration: 0840, time: 31.843 minutes, Δt: 517.932 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 1.339 minutes
Iteration: 0880, time: 32.170 minutes, Δt: 487.603 ms, max(|w|) = 4.8e-01 ms⁻¹, wall time: 1.388 minutes
Iteration: 0920, time: 32.463 minutes, Δt: 355.078 ms, max(|w|) = 4.7e-01 ms⁻¹, wall time: 1.437 minutes
Iteration: 0960, time: 32.717 minutes, Δt: 407.330 ms, max(|w|) = 5.4e-01 ms⁻¹, wall time: 1.486 minutes
Iteration: 1000, time: 32.955 minutes, Δt: 351.277 ms, max(|w|) = 6.3e-01 ms⁻¹, wall time: 1.536 minutes
Iteration: 1040, time: 33.186 minutes, Δt: 391.252 ms, max(|w|) = 6.4e-01 ms⁻¹, wall time: 1.604 minutes
Iteration: 1080, time: 33.432 minutes, Δt: 383.399 ms, max(|w|) = 6.2e-01 ms⁻¹, wall time: 1.655 minutes
Iteration: 1120, time: 33.677 minutes, Δt: 377.049 ms, max(|w|) = 6.7e-01 ms⁻¹, wall time: 1.720 minutes
Iteration: 1160, time: 33.941 minutes, Δt: 438.387 ms, max(|w|) = 6.8e-01 ms⁻¹, wall time: 1.780 minutes
Iteration: 1200, time: 34.224 minutes, Δt: 418.475 ms, max(|w|) = 6.6e-01 ms⁻¹, wall time: 1.836 minutes
Iteration: 1240, time: 34.500 minutes, Δt: 470.404 ms, max(|w|) = 6.3e-01 ms⁻¹, wall time: 1.910 minutes
Iteration: 1280, time: 34.795 minutes, Δt: 474.890 ms, max(|w|) = 5.5e-01 ms⁻¹, wall time: 1.978 minutes
Iteration: 1320, time: 35.107 minutes, Δt: 493.981 ms, max(|w|) = 6.0e-01 ms⁻¹, wall time: 2.052 minutes
Iteration: 1360, time: 35.452 minutes, Δt: 549.810 ms, max(|w|) = 5.6e-01 ms⁻¹, wall time: 2.120 minutes
Iteration: 1400, time: 35.807 minutes, Δt: 553.035 ms, max(|w|) = 4.8e-01 ms⁻¹, wall time: 2.192 minutes
Iteration: 1440, time: 36.177 minutes, Δt: 593.271 ms, max(|w|) = 5.1e-01 ms⁻¹, wall time: 2.261 minutes
Iteration: 1480, time: 36.533 minutes, Δt: 522.404 ms, max(|w|) = 5.2e-01 ms⁻¹, wall time: 2.337 minutes
Iteration: 1520, time: 36.897 minutes, Δt: 608.747 ms, max(|w|) = 4.9e-01 ms⁻¹, wall time: 2.417 minutes
Iteration: 1560, time: 37.285 minutes, Δt: 603.123 ms, max(|w|) = 4.9e-01 ms⁻¹, wall time: 2.487 minutes
Iteration: 1600, time: 37.682 minutes, Δt: 629.158 ms, max(|w|) = 5.3e-01 ms⁻¹, wall time: 2.564 minutes
Iteration: 1640, time: 38.081 minutes, Δt: 644.964 ms, max(|w|) = 4.7e-01 ms⁻¹, wall time: 2.643 minutes
Iteration: 1680, time: 38.510 minutes, Δt: 682.749 ms, max(|w|) = 4.8e-01 ms⁻¹, wall time: 2.710 minutes
Iteration: 1720, time: 38.933 minutes, Δt: 594.718 ms, max(|w|) = 4.4e-01 ms⁻¹, wall time: 2.788 minutes
Iteration: 1760, time: 39.357 minutes, Δt: 669.319 ms, max(|w|) = 4.4e-01 ms⁻¹, wall time: 2.858 minutes
Iteration: 1800, time: 39.787 minutes, Δt: 510.931 ms, max(|w|) = 4.6e-01 ms⁻¹, wall time: 2.908 minutes
Iteration: 1840, time: 40.175 minutes, Δt: 654.375 ms, max(|w|) = 4.4e-01 ms⁻¹, wall time: 2.965 minutes
Iteration: 1880, time: 40.631 minutes, Δt: 662.104 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 3.027 minutes
Iteration: 1920, time: 41.067 minutes, Δt: 687.888 ms, max(|w|) = 4.5e-01 ms⁻¹, wall time: 3.097 minutes
Iteration: 1960, time: 41.523 minutes, Δt: 715.682 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 3.166 minutes
Iteration: 2000, time: 41.997 minutes, Δt: 637.011 ms, max(|w|) = 4.5e-01 ms⁻¹, wall time: 3.232 minutes
Iteration: 2040, time: 42.419 minutes, Δt: 695.873 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 3.315 minutes
Iteration: 2080, time: 42.891 minutes, Δt: 676.025 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 3.382 minutes
Iteration: 2120, time: 43.342 minutes, Δt: 731.164 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 3.465 minutes
Iteration: 2160, time: 43.813 minutes, Δt: 714.762 ms, max(|w|) = 4.5e-01 ms⁻¹, wall time: 3.531 minutes
Iteration: 2200, time: 44.285 minutes, Δt: 729.929 ms, max(|w|) = 4.5e-01 ms⁻¹, wall time: 3.616 minutes
Iteration: 2240, time: 44.760 minutes, Δt: 720.425 ms, max(|w|) = 4.6e-01 ms⁻¹, wall time: 3.684 minutes
Iteration: 2280, time: 45.235 minutes, Δt: 675.444 ms, max(|w|) = 4.6e-01 ms⁻¹, wall time: 3.768 minutes
Iteration: 2320, time: 45.719 minutes, Δt: 754.475 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 3.852 minutes
Iteration: 2360, time: 46.192 minutes, Δt: 737.479 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 3.918 minutes
Iteration: 2400, time: 46.670 minutes, Δt: 717.219 ms, max(|w|) = 4.5e-01 ms⁻¹, wall time: 4.003 minutes
Iteration: 2440, time: 47.139 minutes, Δt: 631.497 ms, max(|w|) = 4.8e-01 ms⁻¹, wall time: 4.087 minutes
Iteration: 2480, time: 47.602 minutes, Δt: 741.010 ms, max(|w|) = 4.6e-01 ms⁻¹, wall time: 4.154 minutes
Iteration: 2520, time: 48.098 minutes, Δt: 757.251 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 4.237 minutes
Iteration: 2560, time: 48.587 minutes, Δt: 671.250 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 4.320 minutes
Iteration: 2600, time: 49.063 minutes, Δt: 738.081 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 4.370 minutes
Iteration: 2640, time: 49.552 minutes, Δt: 767.408 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 4.420 minutes
Iteration: 2680, time: 50.061 minutes, Δt: 800.145 ms, max(|w|) = 4.2e-01 ms⁻¹, wall time: 4.470 minutes
Iteration: 2720, time: 50.568 minutes, Δt: 729.699 ms, max(|w|) = 4.2e-01 ms⁻¹, wall time: 4.520 minutes
Iteration: 2760, time: 51.064 minutes, Δt: 792.330 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 4.571 minutes
Iteration: 2800, time: 51.573 minutes, Δt: 699.598 ms, max(|w|) = 4.2e-01 ms⁻¹, wall time: 4.621 minutes
Iteration: 2840, time: 52.046 minutes, Δt: 751.590 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 4.682 minutes
Iteration: 2880, time: 52.529 minutes, Δt: 789.305 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 4.732 minutes
Iteration: 2920, time: 53.040 minutes, Δt: 827.631 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 4.783 minutes
Iteration: 2960, time: 53.565 minutes, Δt: 680.612 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 4.837 minutes
Iteration: 3000, time: 54.037 minutes, Δt: 761.112 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 4.896 minutes
Iteration: 3040, time: 54.558 minutes, Δt: 677.277 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 4.947 minutes
Iteration: 3080, time: 55.050 minutes, Δt: 747.585 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 4.998 minutes
Iteration: 3120, time: 55.562 minutes, Δt: 753.628 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.047 minutes
Iteration: 3160, time: 56.064 minutes, Δt: 755.103 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 5.103 minutes
Iteration: 3200, time: 56.568 minutes, Δt: 781.773 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 5.164 minutes
Iteration: 3240, time: 57.098 minutes, Δt: 777.922 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 5.224 minutes
Iteration: 3280, time: 57.616 minutes, Δt: 823.830 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 5.273 minutes
Iteration: 3320, time: 58.146 minutes, Δt: 746.093 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 5.338 minutes
Iteration: 3360, time: 58.664 minutes, Δt: 817.064 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 5.395 minutes
Iteration: 3400, time: 59.176 minutes, Δt: 844.497 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 5.455 minutes
Iteration: 3440, time: 59.677 minutes, Δt: 766.616 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.517 minutes
Iteration: 3480, time: 1.003 hours, Δt: 777.111 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 5.567 minutes
Iteration: 3520, time: 1.012 hours, Δt: 826.922 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 5.616 minutes
Iteration: 3560, time: 1.020 hours, Δt: 836.524 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.666 minutes
Iteration: 3600, time: 1.029 hours, Δt: 735.153 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.717 minutes
Iteration: 3640, time: 1.037 hours, Δt: 741.918 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.766 minutes
Iteration: 3680, time: 1.046 hours, Δt: 820.122 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 5.815 minutes
Iteration: 3720, time: 1.055 hours, Δt: 745.745 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 5.866 minutes
Iteration: 3760, time: 1.064 hours, Δt: 829.081 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 5.916 minutes
Iteration: 3800, time: 1.072 hours, Δt: 816.227 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 5.969 minutes
Iteration: 3840, time: 1.081 hours, Δt: 841.619 ms, max(|w|) = 3.3e-01 ms⁻¹, wall time: 6.029 minutes
Iteration: 3880, time: 1.090 hours, Δt: 814.088 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 6.082 minutes
Iteration: 3920, time: 1.099 hours, Δt: 834.629 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 6.133 minutes
Iteration: 3960, time: 1.108 hours, Δt: 752.234 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 6.183 minutes
Iteration: 4000, time: 1.117 hours, Δt: 846.879 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 6.237 minutes
Iteration: 4040, time: 1.126 hours, Δt: 810.276 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 6.287 minutes
Iteration: 4080, time: 1.135 hours, Δt: 820.967 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 6.340 minutes
Iteration: 4120, time: 1.144 hours, Δt: 779.307 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 6.403 minutes
Iteration: 4160, time: 1.153 hours, Δt: 843.781 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 6.456 minutes
Iteration: 4200, time: 1.162 hours, Δt: 755.839 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 6.505 minutes
Iteration: 4240, time: 1.171 hours, Δt: 732.757 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 6.555 minutes
Iteration: 4280, time: 1.179 hours, Δt: 773.174 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 6.608 minutes
Iteration: 4320, time: 1.188 hours, Δt: 772.131 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 6.669 minutes
Iteration: 4360, time: 1.197 hours, Δt: 789.325 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 6.730 minutes
Iteration: 4400, time: 1.205 hours, Δt: 830.198 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 6.792 minutes
Iteration: 4440, time: 1.215 hours, Δt: 760.901 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 6.853 minutes
Iteration: 4480, time: 1.223 hours, Δt: 788.759 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 6.915 minutes
Iteration: 4520, time: 1.232 hours, Δt: 805.510 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 6.977 minutes
Iteration: 4560, time: 1.241 hours, Δt: 809.557 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 7.039 minutes
Iteration: 4600, time: 1.249 hours, Δt: 768.733 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 7.101 minutes
Iteration: 4640, time: 1.258 hours, Δt: 788.153 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 7.162 minutes
Iteration: 4680, time: 1.267 hours, Δt: 854.543 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 7.226 minutes
Iteration: 4720, time: 1.276 hours, Δt: 864.240 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 7.278 minutes
Iteration: 4760, time: 1.285 hours, Δt: 819.206 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 7.331 minutes
Iteration: 4800, time: 1.294 hours, Δt: 800.575 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 7.381 minutes
Iteration: 4840, time: 1.302 hours, Δt: 817.653 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 7.432 minutes
Iteration: 4880, time: 1.311 hours, Δt: 790.281 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 7.482 minutes
Iteration: 4920, time: 1.319 hours, Δt: 788.025 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 7.534 minutes
Iteration: 4960, time: 1.328 hours, Δt: 761.050 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 7.585 minutes
Iteration: 5000, time: 1.337 hours, Δt: 767.187 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 7.634 minutes
Iteration: 5040, time: 1.346 hours, Δt: 826.103 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 7.684 minutes
Iteration: 5080, time: 1.355 hours, Δt: 832.044 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 7.755 minutes
Iteration: 5120, time: 1.365 hours, Δt: 820.184 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 7.812 minutes
Iteration: 5160, time: 1.373 hours, Δt: 824.984 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 7.863 minutes
Iteration: 5200, time: 1.382 hours, Δt: 846.916 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 7.912 minutes
Iteration: 5240, time: 1.391 hours, Δt: 749.739 ms, max(|w|) = 4.1e-01 ms⁻¹, wall time: 7.963 minutes
Iteration: 5280, time: 1.400 hours, Δt: 828.746 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 8.013 minutes
Iteration: 5320, time: 1.409 hours, Δt: 793.905 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 8.067 minutes
Iteration: 5360, time: 1.417 hours, Δt: 810.293 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 8.129 minutes
Iteration: 5400, time: 1.427 hours, Δt: 775.422 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 8.179 minutes
Iteration: 5440, time: 1.435 hours, Δt: 833.111 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 8.229 minutes
Iteration: 5480, time: 1.444 hours, Δt: 831.895 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 8.282 minutes
Iteration: 5520, time: 1.453 hours, Δt: 825.693 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 8.344 minutes
Iteration: 5560, time: 1.462 hours, Δt: 753.304 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 8.404 minutes
Iteration: 5600, time: 1.470 hours, Δt: 823.050 ms, max(|w|) = 3.3e-01 ms⁻¹, wall time: 8.469 minutes
Iteration: 5640, time: 1.479 hours, Δt: 839.502 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 8.525 minutes
Iteration: 5680, time: 1.488 hours, Δt: 794.936 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 8.576 minutes
Iteration: 5720, time: 1.497 hours, Δt: 782.462 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 8.626 minutes
Iteration: 5760, time: 1.506 hours, Δt: 757.295 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 8.679 minutes
Iteration: 5800, time: 1.514 hours, Δt: 722.865 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 8.730 minutes
Iteration: 5840, time: 1.523 hours, Δt: 825.754 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 8.780 minutes
Iteration: 5880, time: 1.532 hours, Δt: 876.576 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 8.838 minutes
Iteration: 5920, time: 1.541 hours, Δt: 834.360 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 8.898 minutes
Iteration: 5960, time: 1.550 hours, Δt: 851.988 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 8.948 minutes
Iteration: 6000, time: 1.558 hours, Δt: 765.248 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 8.999 minutes
Iteration: 6040, time: 1.567 hours, Δt: 830.537 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 9.051 minutes
Iteration: 6080, time: 1.576 hours, Δt: 750.394 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 9.108 minutes
Iteration: 6120, time: 1.584 hours, Δt: 794.321 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 9.166 minutes
Iteration: 6160, time: 1.593 hours, Δt: 828.089 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 9.216 minutes
Iteration: 6200, time: 1.602 hours, Δt: 793.012 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 9.268 minutes
Iteration: 6240, time: 1.611 hours, Δt: 830.145 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 9.321 minutes
Iteration: 6280, time: 1.619 hours, Δt: 800.224 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 9.384 minutes
Iteration: 6320, time: 1.628 hours, Δt: 746.209 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 9.445 minutes
Iteration: 6360, time: 1.637 hours, Δt: 813.058 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 9.504 minutes
Iteration: 6400, time: 1.646 hours, Δt: 799.827 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 9.557 minutes
Iteration: 6440, time: 1.655 hours, Δt: 874.689 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 9.609 minutes
Iteration: 6480, time: 1.664 hours, Δt: 822.240 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 9.662 minutes
Iteration: 6520, time: 1.673 hours, Δt: 755.122 ms, max(|w|) = 3.3e-01 ms⁻¹, wall time: 9.714 minutes
Iteration: 6560, time: 1.681 hours, Δt: 828.405 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 9.768 minutes
Iteration: 6600, time: 1.690 hours, Δt: 796.703 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 9.822 minutes
Iteration: 6640, time: 1.699 hours, Δt: 773.427 ms, max(|w|) = 4.0e-01 ms⁻¹, wall time: 9.874 minutes
Iteration: 6680, time: 1.708 hours, Δt: 834.473 ms, max(|w|) = 3.3e-01 ms⁻¹, wall time: 9.926 minutes
Iteration: 6720, time: 1.716 hours, Δt: 691.393 ms, max(|w|) = 3.3e-01 ms⁻¹, wall time: 9.983 minutes
Iteration: 6760, time: 1.724 hours, Δt: 717.311 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 10.044 minutes
Iteration: 6800, time: 1.733 hours, Δt: 819.433 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 10.106 minutes
Iteration: 6840, time: 1.742 hours, Δt: 824.103 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 10.160 minutes
Iteration: 6880, time: 1.751 hours, Δt: 754.546 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 10.226 minutes
Iteration: 6920, time: 1.760 hours, Δt: 823.218 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 10.283 minutes
Iteration: 6960, time: 1.768 hours, Δt: 745.186 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 10.337 minutes
Iteration: 7000, time: 1.777 hours, Δt: 800.973 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 10.388 minutes
Iteration: 7040, time: 1.786 hours, Δt: 810.998 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 10.440 minutes
Iteration: 7080, time: 1.794 hours, Δt: 788.281 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 10.490 minutes
Iteration: 7120, time: 1.803 hours, Δt: 851.213 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 10.554 minutes
Iteration: 7160, time: 1.812 hours, Δt: 802.340 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 10.613 minutes
Iteration: 7200, time: 1.820 hours, Δt: 743.268 ms, max(|w|) = 4.2e-01 ms⁻¹, wall time: 10.664 minutes
Iteration: 7240, time: 1.829 hours, Δt: 813.628 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 10.716 minutes
Iteration: 7280, time: 1.837 hours, Δt: 819.806 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 10.767 minutes
Iteration: 7320, time: 1.846 hours, Δt: 799.818 ms, max(|w|) = 3.9e-01 ms⁻¹, wall time: 10.821 minutes
Iteration: 7360, time: 1.855 hours, Δt: 775.208 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 10.892 minutes
Iteration: 7400, time: 1.864 hours, Δt: 831.615 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 10.953 minutes
Iteration: 7440, time: 1.873 hours, Δt: 720.380 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 11.015 minutes
Iteration: 7480, time: 1.881 hours, Δt: 747.133 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 11.074 minutes
Iteration: 7520, time: 1.890 hours, Δt: 767.595 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 11.124 minutes
Iteration: 7560, time: 1.898 hours, Δt: 783.367 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 11.175 minutes
Iteration: 7600, time: 1.907 hours, Δt: 740.508 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 11.229 minutes
Iteration: 7640, time: 1.916 hours, Δt: 799.475 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 11.280 minutes
Iteration: 7680, time: 1.925 hours, Δt: 776.045 ms, max(|w|) = 4.2e-01 ms⁻¹, wall time: 11.336 minutes
Iteration: 7720, time: 1.933 hours, Δt: 798.718 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 11.393 minutes
Iteration: 7760, time: 1.942 hours, Δt: 787.177 ms, max(|w|) = 3.8e-01 ms⁻¹, wall time: 11.447 minutes
Iteration: 7800, time: 1.950 hours, Δt: 776.460 ms, max(|w|) = 3.6e-01 ms⁻¹, wall time: 11.506 minutes
Iteration: 7840, time: 1.958 hours, Δt: 718.729 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 11.570 minutes
Iteration: 7880, time: 1.967 hours, Δt: 810.960 ms, max(|w|) = 4.3e-01 ms⁻¹, wall time: 11.626 minutes
Iteration: 7920, time: 1.975 hours, Δt: 753.317 ms, max(|w|) = 3.7e-01 ms⁻¹, wall time: 11.687 minutes
Iteration: 7960, time: 1.984 hours, Δt: 790.678 ms, max(|w|) = 3.5e-01 ms⁻¹, wall time: 11.741 minutes
Iteration: 8000, time: 1.993 hours, Δt: 747.771 ms, max(|w|) = 3.4e-01 ms⁻¹, wall time: 11.795 minutes
[ Info: Simulation is stopping after running for 11.866 minutes.
[ Info: Simulation time 2 hours equals or exceeds stop time 2 hours.

Turbulence visualization

We animate the data saved in ocean_wind_mixing_and_convection.jld2. We prepare for animating the flow by loading the data into FieldTimeSeries and defining functions for computing colorbar limits.

filepath = filename * ".jld2"

time_series = (w = FieldTimeSeries(filepath, "w"),
               T = FieldTimeSeries(filepath, "T"),
               S = FieldTimeSeries(filepath, "S"),
               νₑ = FieldTimeSeries(filepath, "νₑ"))
(w = 128×1×65×121 FieldTimeSeries{InMemory} located at (Center, Center, Face) of w at ocean_wind_mixing_and_convection.jld2
├── grid: 128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── indices: (:, 64:64, :)
├── time_indexing: Linear()
├── backend: InMemory()
├── path: ocean_wind_mixing_and_convection.jld2
├── name: w
└── data: 134×1×71×121 OffsetArray(::Array{Float64, 4}, -2:131, 64:64, -2:68, 1:121) with eltype Float64 with indices -2:131×64:64×-2:68×1:121
    └── max=0.414299, min=-0.49242, mean=3.10934e-5, T = 128×1×64×121 FieldTimeSeries{InMemory} located at (Center, Center, Center) of T at ocean_wind_mixing_and_convection.jld2
├── grid: 128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── indices: (:, 64:64, :)
├── time_indexing: Linear()
├── backend: InMemory()
├── path: ocean_wind_mixing_and_convection.jld2
├── name: T
└── data: 134×1×70×121 OffsetArray(::Array{Float64, 4}, -2:131, 64:64, -2:67, 1:121) with eltype Float64 with indices -2:131×64:64×-2:67×1:121
    └── max=20.1229, min=0.0, mean=18.5882, S = 128×1×64×121 FieldTimeSeries{InMemory} located at (Center, Center, Center) of S at ocean_wind_mixing_and_convection.jld2
├── grid: 128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── indices: (:, 64:64, :)
├── time_indexing: Linear()
├── backend: InMemory()
├── path: ocean_wind_mixing_and_convection.jld2
├── name: S
└── data: 134×1×70×121 OffsetArray(::Array{Float64, 4}, -2:131, 64:64, -2:67, 1:121) with eltype Float64 with indices -2:131×64:64×-2:67×1:121
    └── max=35.0349, min=0.0, mean=33.0007, νₑ = 128×1×64×121 FieldTimeSeries{InMemory} located at (Center, Center, Center) of νₑ at ocean_wind_mixing_and_convection.jld2
├── grid: 128×128×64 RectilinearGrid{Float64, Periodic, Periodic, Bounded} on CPU with 3×3×3 halo
├── indices: (:, 64:64, :)
├── time_indexing: Linear()
├── backend: InMemory()
├── path: ocean_wind_mixing_and_convection.jld2
├── name: νₑ
└── data: 134×1×70×121 OffsetArray(::Array{Float64, 4}, -2:131, 64:64, -2:67, 1:121) with eltype Float64 with indices -2:131×64:64×-2:67×1:121
    └── max=0.0, min=0.0, mean=0.0)

We start the animation at $t = 10$ minutes since things are pretty boring till then:

times = time_series.w.times
intro = searchsortedfirst(times, 10minutes)
11

We are now ready to animate using Makie. We use Makie's Observable to animate the data. To dive into how Observables work we refer to Makie.jl's Documentation.

n = Observable(intro)

 wₙ = @lift time_series.w[$n]
 Tₙ = @lift time_series.T[$n]
 Sₙ = @lift time_series.S[$n]
νₑₙ = @lift time_series.νₑ[$n]

fig = Figure(size = (1800, 900))

axis_kwargs = (xlabel="x (m)",
               ylabel="z (m)",
               aspect = AxisAspect(grid.Lx/grid.Lz),
               limits = ((0, grid.Lx), (-grid.Lz, 0)))

ax_w  = Axis(fig[2, 1]; title = "Vertical velocity", axis_kwargs...)
ax_T  = Axis(fig[2, 3]; title = "Temperature", axis_kwargs...)
ax_S  = Axis(fig[3, 1]; title = "Salinity", axis_kwargs...)
ax_νₑ = Axis(fig[3, 3]; title = "Eddy viscocity", axis_kwargs...)

title = @lift @sprintf("t = %s", prettytime(times[$n]))

 wlims = (-0.05, 0.05)
 Tlims = (19.7, 19.99)
 Slims = (35, 35.005)
νₑlims = (1e-6, 5e-3)

hm_w = heatmap!(ax_w, wₙ; colormap = :balance, colorrange = wlims)
Colorbar(fig[2, 2], hm_w; label = "m s⁻¹")

hm_T = heatmap!(ax_T, Tₙ; colormap = :thermal, colorrange = Tlims)
Colorbar(fig[2, 4], hm_T; label = "ᵒC")

hm_S = heatmap!(ax_S, Sₙ; colormap = :haline, colorrange = Slims)
Colorbar(fig[3, 2], hm_S; label = "g / kg")

hm_νₑ = heatmap!(ax_νₑ, νₑₙ; colormap = :thermal, colorrange = νₑlims)
Colorbar(fig[3, 4], hm_νₑ; label = "m s⁻²")

fig[1, 1:4] = Label(fig, title, fontsize=24, tellwidth=false)

fig

And now record a movie.

frames = intro:length(times)

@info "Making a motion picture of ocean wind mixing and convection..."

CairoMakie.record(fig, filename * ".mp4", frames, framerate=8) do i
    n[] = i
end
[ Info: Making a motion picture of ocean wind mixing and convection...


Julia version and environment information

This example was executed with the following version of Julia:

using InteractiveUtils: versioninfo
versioninfo()
Julia Version 1.12.4
Commit 01a2eadb047 (2026-01-06 16:56 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 128 × AMD EPYC 9374F 32-Core Processor
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, znver4)
  GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 128 virtual cores)
Environment:
  LD_LIBRARY_PATH = 
  JULIA_PKG_SERVER_REGISTRY_PREFERENCE = eager
  JULIA_DEPOT_PATH = /var/lib/buildkite-agent/.julia-oceananigans
  JULIA_PROJECT = /var/lib/buildkite-agent/Oceananigans.jl-28839/docs/
  JULIA_VERSION = 1.12.4
  JULIA_LOAD_PATH = @:@v#.#:@stdlib
  JULIA_VERSION_ENZYME = 1.10.10
  JULIA_PYTHONCALL_EXE = /var/lib/buildkite-agent/Oceananigans.jl-28839/docs/.CondaPkg/.pixi/envs/default/bin/python
  JULIA_DEBUG = Literate

These were the top-level packages installed in the environment:

import Pkg
Pkg.status()
Status `~/Oceananigans.jl-28839/docs/Project.toml`
  [79e6a3ab] Adapt v4.4.0
  [052768ef] CUDA v5.9.6
  [13f3f980] CairoMakie v0.15.8
  [e30172f5] Documenter v1.16.1
  [daee34ce] DocumenterCitations v1.4.1
  [033835bb] JLD2 v0.6.3
  [63c18a36] KernelAbstractions v0.9.39
  [98b081ad] Literate v2.21.0
  [da04e1cc] MPI v0.20.23
  [85f8d34a] NCDatasets v0.14.10
  [9e8cae18] Oceananigans v0.104.2 `..`
  [f27b6e38] Polynomials v4.1.0
  [6038ab10] Rotations v1.7.1
  [d496a93d] SeawaterPolynomials v0.3.10
  [09ab397b] StructArrays v0.7.2
  [bdfc003b] TimesDates v0.3.3
  [2e0b0046] XESMF v0.1.6
  [b77e0a4c] InteractiveUtils v1.11.0
  [37e2e46d] LinearAlgebra v1.12.0
  [44cfe95a] Pkg v1.12.1

This page was generated using Literate.jl.