Baroclinic adjustment
In this example, we simulate the evolution and equilibration of a baroclinically unstable front.
Install dependencies
First let's make sure we have all required packages installed.
using Pkg
pkg"add Oceananigans, CairoMakie"
using Oceananigans
using Oceananigans.Units
Grid
We use a three-dimensional channel that is periodic in the x
direction:
Lx = 1000kilometers # east-west extent [m]
Ly = 1000kilometers # north-south extent [m]
Lz = 1kilometers # depth [m]
grid = RectilinearGrid(size = (48, 48, 8),
x = (0, Lx),
y = (-Ly/2, Ly/2),
z = (-Lz, 0),
topology = (Periodic, Bounded, Bounded))
48×48×8 RectilinearGrid{Float64, Periodic, Bounded, Bounded} on CPU with 3×3×3 halo
├── Periodic x ∈ [0.0, 1.0e6) regularly spaced with Δx=20833.3
├── Bounded y ∈ [-500000.0, 500000.0] regularly spaced with Δy=20833.3
└── Bounded z ∈ [-1000.0, 0.0] regularly spaced with Δz=125.0
Model
We built a HydrostaticFreeSurfaceModel
with an ImplicitFreeSurface
solver. Regarding Coriolis, we use a beta-plane centered at 45° South.
model = HydrostaticFreeSurfaceModel(; grid,
coriolis = BetaPlane(latitude = -45),
buoyancy = BuoyancyTracer(),
tracers = :b,
momentum_advection = WENO(),
tracer_advection = WENO())
HydrostaticFreeSurfaceModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
├── grid: 48×48×8 RectilinearGrid{Float64, Periodic, Bounded, Bounded} on CPU with 3×3×3 halo
├── timestepper: QuasiAdamsBashforth2TimeStepper
├── tracers: b
├── closure: Nothing
├── buoyancy: BuoyancyTracer with ĝ = NegativeZDirection()
├── free surface: ImplicitFreeSurface with gravitational acceleration 9.80665 m s⁻²
│ └── solver: FFTImplicitFreeSurfaceSolver
├── advection scheme:
│ ├── momentum: WENO(order=5)
│ └── b: WENO(order=5)
└── coriolis: BetaPlane{Float64}
We start our simulation from rest with a baroclinically unstable buoyancy distribution. We use ramp(y, Δy)
, defined below, to specify a front with width Δy
and horizontal buoyancy gradient M²
. We impose the front on top of a vertical buoyancy gradient N²
and a bit of noise.
"""
ramp(y, Δy)
Linear ramp from 0 to 1 between -Δy/2 and +Δy/2.
For example:
```
y < -Δy/2 => ramp = 0
-Δy/2 < y < -Δy/2 => ramp = y / Δy
y > Δy/2 => ramp = 1
```
"""
ramp(y, Δy) = min(max(0, y/Δy + 1/2), 1)
N² = 1e-5 # [s⁻²] buoyancy frequency / stratification
M² = 1e-7 # [s⁻²] horizontal buoyancy gradient
Δy = 100kilometers # width of the region of the front
Δb = Δy * M² # buoyancy jump associated with the front
ϵb = 1e-2 * Δb # noise amplitude
bᵢ(x, y, z) = N² * z + Δb * ramp(y, Δy) + ϵb * randn()
set!(model, b=bᵢ)
Let's visualize the initial buoyancy distribution.
using CairoMakie
# Build coordinates with units of kilometers
x, y, z = 1e-3 .* nodes(grid, (Center(), Center(), Center()))
b = model.tracers.b
fig, ax, hm = heatmap(view(b, 1, :, :),
colormap = :deep,
axis = (xlabel = "y [km]",
ylabel = "z [km]",
title = "b(x=0, y, z, t=0)",
titlesize = 24))
Colorbar(fig[1, 2], hm, label = "[m s⁻²]")
fig
Simulation
Now let's build a Simulation
.
simulation = Simulation(model, Δt=20minutes, stop_time=20days)
Simulation of HydrostaticFreeSurfaceModel{CPU, RectilinearGrid}(time = 0 seconds, iteration = 0)
├── Next time step: 20 minutes
├── Elapsed wall time: 0 seconds
├── Wall time per iteration: NaN days
├── Stop time: 20 days
├── 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
└── Diagnostics: OrderedDict with no entries
We add a TimeStepWizard
callback to adapt the simulation's time-step,
conjure_time_step_wizard!(simulation, IterationInterval(20), cfl=0.2, max_Δt=20minutes)
Also, we add a callback to print a message about how the simulation is going,
using Printf
wall_clock = Ref(time_ns())
function print_progress(sim)
u, v, w = model.velocities
progress = 100 * (time(sim) / sim.stop_time)
elapsed = (time_ns() - wall_clock[]) / 1e9
@printf("[%05.2f%%] i: %d, t: %s, wall time: %s, max(u): (%6.3e, %6.3e, %6.3e) m/s, next Δt: %s\n",
progress, iteration(sim), prettytime(sim), prettytime(elapsed),
maximum(abs, u), maximum(abs, v), maximum(abs, w), prettytime(sim.Δt))
wall_clock[] = time_ns()
return nothing
end
add_callback!(simulation, print_progress, IterationInterval(100))
Diagnostics/Output
Here, we save the buoyancy, $b$, at the edges of our domain as well as the zonal ($x$) average of buoyancy.
u, v, w = model.velocities
ζ = ∂x(v) - ∂y(u)
B = Average(b, dims=1)
U = Average(u, dims=1)
V = Average(v, dims=1)
filename = "baroclinic_adjustment"
save_fields_interval = 0.5day
slicers = (east = (grid.Nx, :, :),
north = (:, grid.Ny, :),
bottom = (:, :, 1),
top = (:, :, grid.Nz))
for side in keys(slicers)
indices = slicers[side]
simulation.output_writers[side] = JLD2Writer(model, (; b, ζ);
filename = filename * "_$(side)_slice",
schedule = TimeInterval(save_fields_interval),
overwrite_existing = true,
indices)
end
simulation.output_writers[:zonal] = JLD2Writer(model, (; b=B, u=U, v=V);
filename = filename * "_zonal_average",
schedule = TimeInterval(save_fields_interval),
overwrite_existing = true)
JLD2Writer scheduled on TimeInterval(12 hours):
├── filepath: baroclinic_adjustment_zonal_average.jld2
├── 3 outputs: (b, u, v)
├── array type: Array{Float32}
├── including: [:grid, :coriolis, :buoyancy, :closure]
├── file_splitting: NoFileSplitting
└── file size: 32.5 KiB
Now we're ready to run.
@info "Running the simulation..."
run!(simulation)
@info "Simulation completed in " * prettytime(simulation.run_wall_time)
[ Info: Running the simulation...
[ Info: Initializing simulation...
[00.00%] i: 0, t: 0 seconds, wall time: 28.282 seconds, max(u): (0.000e+00, 0.000e+00, 0.000e+00) m/s, next Δt: 20 minutes
[ Info: ... simulation initialization complete (26.462 seconds)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (19.073 seconds).
[06.94%] i: 100, t: 1.389 days, wall time: 39.350 seconds, max(u): (1.296e-01, 1.203e-01, 1.674e-03) m/s, next Δt: 20 minutes
[13.89%] i: 200, t: 2.778 days, wall time: 646.880 ms, max(u): (2.218e-01, 1.810e-01, 1.743e-03) m/s, next Δt: 20 minutes
[20.83%] i: 300, t: 4.167 days, wall time: 698.864 ms, max(u): (3.027e-01, 2.268e-01, 1.832e-03) m/s, next Δt: 20 minutes
[27.78%] i: 400, t: 5.556 days, wall time: 594.215 ms, max(u): (3.627e-01, 2.779e-01, 1.653e-03) m/s, next Δt: 20 minutes
[34.72%] i: 500, t: 6.944 days, wall time: 712.845 ms, max(u): (4.524e-01, 3.763e-01, 1.683e-03) m/s, next Δt: 20 minutes
[41.67%] i: 600, t: 8.333 days, wall time: 753.448 ms, max(u): (5.803e-01, 5.695e-01, 2.109e-03) m/s, next Δt: 20 minutes
[48.61%] i: 700, t: 9.722 days, wall time: 566.057 ms, max(u): (7.956e-01, 9.083e-01, 2.886e-03) m/s, next Δt: 20 minutes
[55.56%] i: 800, t: 11.111 days, wall time: 649.804 ms, max(u): (1.112e+00, 1.194e+00, 4.016e-03) m/s, next Δt: 20 minutes
[62.50%] i: 900, t: 12.500 days, wall time: 586.066 ms, max(u): (1.346e+00, 1.208e+00, 4.384e-03) m/s, next Δt: 20 minutes
[69.44%] i: 1000, t: 13.889 days, wall time: 639.361 ms, max(u): (1.423e+00, 1.290e+00, 5.343e-03) m/s, next Δt: 20 minutes
[76.39%] i: 1100, t: 15.278 days, wall time: 655.938 ms, max(u): (1.395e+00, 1.009e+00, 3.161e-03) m/s, next Δt: 20 minutes
[83.33%] i: 1200, t: 16.667 days, wall time: 632.549 ms, max(u): (1.317e+00, 9.818e-01, 2.676e-03) m/s, next Δt: 20 minutes
[90.28%] i: 1300, t: 18.056 days, wall time: 627.335 ms, max(u): (1.252e+00, 1.035e+00, 3.602e-03) m/s, next Δt: 20 minutes
[97.22%] i: 1400, t: 19.444 days, wall time: 567.814 ms, max(u): (1.243e+00, 1.211e+00, 2.564e-03) m/s, next Δt: 20 minutes
[ Info: Simulation is stopping after running for 58.320 seconds.
[ Info: Simulation time 20 days equals or exceeds stop time 20 days.
[ Info: Simulation completed in 58.352 seconds
Visualization
All that's left is to make a pretty movie. Actually, we make two visualizations here. First, we illustrate how to make a 3D visualization with Makie
's Axis3
and Makie.surface
. Then we make a movie in 2D. We use CairoMakie
in this example, but note that using GLMakie
is more convenient on a system with OpenGL, as figures will be displayed on the screen.
using CairoMakie
Three-dimensional visualization
We load the saved buoyancy output on the top, north, and east surface as FieldTimeSeries
es.
filename = "baroclinic_adjustment"
sides = keys(slicers)
slice_filenames = NamedTuple(side => filename * "_$(side)_slice.jld2" for side in sides)
b_timeserieses = (east = FieldTimeSeries(slice_filenames.east, "b"),
north = FieldTimeSeries(slice_filenames.north, "b"),
top = FieldTimeSeries(slice_filenames.top, "b"))
B_timeseries = FieldTimeSeries(filename * "_zonal_average.jld2", "b")
times = B_timeseries.times
grid = B_timeseries.grid
48×48×8 RectilinearGrid{Float64, Periodic, Bounded, Bounded} on CPU with 3×3×3 halo
├── Periodic x ∈ [0.0, 1.0e6) regularly spaced with Δx=20833.3
├── Bounded y ∈ [-500000.0, 500000.0] regularly spaced with Δy=20833.3
└── Bounded z ∈ [-1000.0, 0.0] regularly spaced with Δz=125.0
We build the coordinates. We rescale horizontal coordinates to kilometers.
xb, yb, zb = nodes(b_timeserieses.east)
xb = xb ./ 1e3 # convert m -> km
yb = yb ./ 1e3 # convert m -> km
Nx, Ny, Nz = size(grid)
x_xz = repeat(x, 1, Nz)
y_xz_north = y[end] * ones(Nx, Nz)
z_xz = repeat(reshape(z, 1, Nz), Nx, 1)
x_yz_east = x[end] * ones(Ny, Nz)
y_yz = repeat(y, 1, Nz)
z_yz = repeat(reshape(z, 1, Nz), grid.Ny, 1)
x_xy = x
y_xy = y
z_xy_top = z[end] * ones(grid.Nx, grid.Ny)
Then we create a 3D axis. We use zonal_slice_displacement
to control where the plot of the instantaneous zonal average flow is located.
fig = Figure(size = (1600, 800))
zonal_slice_displacement = 1.2
ax = Axis3(fig[2, 1],
aspect=(1, 1, 1/5),
xlabel = "x (km)",
ylabel = "y (km)",
zlabel = "z (m)",
xlabeloffset = 100,
ylabeloffset = 100,
zlabeloffset = 100,
limits = ((x[1], zonal_slice_displacement * x[end]), (y[1], y[end]), (z[1], z[end])),
elevation = 0.45,
azimuth = 6.8,
xspinesvisible = false,
zgridvisible = false,
protrusions = 40,
perspectiveness = 0.7)
Axis3()
We use data from the final savepoint for the 3D plot. Note that this plot can easily be animated by using Makie's Observable
. To dive into Observable
s, check out Makie.jl's Documentation.
n = length(times)
41
Now let's make a 3D plot of the buoyancy and in front of it we'll use the zonally-averaged output to plot the instantaneous zonal-average of the buoyancy.
b_slices = (east = interior(b_timeserieses.east[n], 1, :, :),
north = interior(b_timeserieses.north[n], :, 1, :),
top = interior(b_timeserieses.top[n], :, :, 1))
# Zonally-averaged buoyancy
B = interior(B_timeseries[n], 1, :, :)
clims = 1.1 .* extrema(b_timeserieses.top[n][:])
kwargs = (colorrange=clims, colormap=:deep, shading=NoShading)
surface!(ax, x_yz_east, y_yz, z_yz; color = b_slices.east, kwargs...)
surface!(ax, x_xz, y_xz_north, z_xz; color = b_slices.north, kwargs...)
surface!(ax, x_xy, y_xy, z_xy_top; color = b_slices.top, kwargs...)
sf = surface!(ax, zonal_slice_displacement .* x_yz_east, y_yz, z_yz; color = B, kwargs...)
contour!(ax, y, z, B; transformation = (:yz, zonal_slice_displacement * x[end]),
levels = 15, linewidth = 2, color = :black)
Colorbar(fig[2, 2], sf, label = "m s⁻²", height = Relative(0.4), tellheight=false)
title = "Buoyancy at t = " * string(round(times[n] / day, digits=1)) * " days"
fig[1, 1:2] = Label(fig, title; fontsize = 24, tellwidth = false, padding = (0, 0, -120, 0))
rowgap!(fig.layout, 1, Relative(-0.2))
colgap!(fig.layout, 1, Relative(-0.1))
save("baroclinic_adjustment_3d.png", fig)
Two-dimensional movie
We make a 2D movie that shows buoyancy $b$ and vertical vorticity $ζ$ at the surface, as well as the zonally-averaged zonal and meridional velocities $U$ and $V$ in the $(y, z)$ plane. First we load the FieldTimeSeries
and extract the additional coordinates we'll need for plotting
ζ_timeseries = FieldTimeSeries(slice_filenames.top, "ζ")
U_timeseries = FieldTimeSeries(filename * "_zonal_average.jld2", "u")
B_timeseries = FieldTimeSeries(filename * "_zonal_average.jld2", "b")
V_timeseries = FieldTimeSeries(filename * "_zonal_average.jld2", "v")
xζ, yζ, zζ = nodes(ζ_timeseries)
yv = ynodes(V_timeseries)
xζ = xζ ./ 1e3 # convert m -> km
yζ = yζ ./ 1e3 # convert m -> km
yv = yv ./ 1e3 # convert m -> km
49-element Vector{Float64}:
-500.0
-479.1666666666667
-458.3333333333333
-437.5
-416.6666666666667
-395.8333333333333
-375.0
-354.1666666666667
-333.3333333333333
-312.5
-291.6666666666667
-270.8333333333333
-250.0
-229.16666666666666
-208.33333333333334
-187.5
-166.66666666666666
-145.83333333333334
-125.0
-104.16666666666667
-83.33333333333333
-62.5
-41.666666666666664
-20.833333333333332
0.0
20.833333333333332
41.666666666666664
62.5
83.33333333333333
104.16666666666667
125.0
145.83333333333334
166.66666666666666
187.5
208.33333333333334
229.16666666666666
250.0
270.8333333333333
291.6666666666667
312.5
333.3333333333333
354.1666666666667
375.0
395.8333333333333
416.6666666666667
437.5
458.3333333333333
479.1666666666667
500.0
Next, we set up a plot with 4 panels. The top panels are large and square, while the bottom panels get a reduced aspect ratio through rowsize!
.
set_theme!(Theme(fontsize=24))
fig = Figure(size=(1800, 1000))
axb = Axis(fig[1, 2], xlabel="x (km)", ylabel="y (km)", aspect=1)
axζ = Axis(fig[1, 3], xlabel="x (km)", ylabel="y (km)", aspect=1, yaxisposition=:right)
axu = Axis(fig[2, 2], xlabel="y (km)", ylabel="z (m)")
axv = Axis(fig[2, 3], xlabel="y (km)", ylabel="z (m)", yaxisposition=:right)
rowsize!(fig.layout, 2, Relative(0.3))
To prepare a plot for animation, we index the timeseries with an Observable
,
n = Observable(1)
b_top = @lift interior(b_timeserieses.top[$n], :, :, 1)
ζ_top = @lift interior(ζ_timeseries[$n], :, :, 1)
U = @lift interior(U_timeseries[$n], 1, :, :)
V = @lift interior(V_timeseries[$n], 1, :, :)
B = @lift interior(B_timeseries[$n], 1, :, :)
Observable([-0.009369018487632275 -0.008111517876386642 -0.0068748402409255505 -0.005631978157907724 -0.004382546059787273 -0.003118394175544381 -0.001850432949140668 -0.0006406552856788039; -0.009348861873149872 -0.008127808570861816 -0.006864055059850216 -0.0056105065159499645 -0.004381920676678419 -0.003118066117167473 -0.001867045764811337 -0.000634699419606477; -0.009347666054964066 -0.008152940310537815 -0.006857429631054401 -0.005634091328829527 -0.0043678912334144115 -0.003135434351861477 -0.0018659181660041213 -0.0006188104744069278; -0.009375330060720444 -0.008113063871860504 -0.006857195403426886 -0.0056275781244039536 -0.0043736775405704975 -0.0031334790401160717 -0.0018690675497055054 -0.0006177856703288853; -0.009381406009197235 -0.00810361746698618 -0.006863011047244072 -0.005637264810502529 -0.004400403238832951 -0.0031246403232216835 -0.001883224118500948 -0.0006143258069641888; -0.009374079294502735 -0.008117753081023693 -0.0068964408710598946 -0.005644787568598986 -0.004385445266962051 -0.0031255914364010096 -0.0018849095795303583 -0.0006363576394505799; -0.009369481354951859 -0.008131148293614388 -0.0068870531395077705 -0.005605274811387062 -0.004373365547508001 -0.003111159661784768 -0.0018853632500395179 -0.0006179705960676074; -0.009379029273986816 -0.008114533498883247 -0.006874439772218466 -0.005626489408314228 -0.004382358398288488 -0.003123447997495532 -0.001873103785328567 -0.0006321794353425503; -0.009383120574057102 -0.008132620714604855 -0.006878598127514124 -0.0056258440017700195 -0.004363381303846836 -0.003107113763689995 -0.0018875321839004755 -0.0006287647993303835; -0.009355016984045506 -0.008126301690936089 -0.006852097809314728 -0.005607440136373043 -0.004338280763477087 -0.0031399766448885202 -0.0018481413135305047 -0.0006295588682405651; -0.009379039518535137 -0.008112160488963127 -0.006875012069940567 -0.005638361908495426 -0.0043848599307239056 -0.0031046997755765915 -0.00189638149458915 -0.0006373162032105029; -0.009347290731966496 -0.008141803555190563 -0.006889060139656067 -0.005625506397336721 -0.004380079917609692 -0.0031060464680194855 -0.0018776414217427373 -0.0006161659839563072; -0.009372448548674583 -0.008123952895402908 -0.006882156245410442 -0.005632718559354544 -0.004357637371867895 -0.003122255438938737 -0.0018819683464244008 -0.0006211057188920677; -0.009379025548696518 -0.008146035484969616 -0.006922862026840448 -0.00564224598929286 -0.004362132865935564 -0.0031303756404668093 -0.0018716457998380065 -0.0006322068511508405; -0.009407946839928627 -0.008097164332866669 -0.006876867730170488 -0.005606839898973703 -0.004363661631941795 -0.003112134523689747 -0.0018530513625591993 -0.0006243163952603936; -0.009353888221085072 -0.008123256266117096 -0.006882857531309128 -0.005599049385637045 -0.004370411857962608 -0.0031262340489774942 -0.001856622169725597 -0.000635304837487638; -0.009416593238711357 -0.00814185943454504 -0.006858795881271362 -0.0056321569718420506 -0.004355624318122864 -0.0031141394283622503 -0.0018800421385094523 -0.0006429331842809916; -0.009377870708703995 -0.008126208558678627 -0.00687306746840477 -0.005625004414469004 -0.004381966777145863 -0.0031187653075903654 -0.0018379923421889544 -0.0006397792021743953; -0.009372561238706112 -0.00811011716723442 -0.0068788169883191586 -0.005624245852231979 -0.004375393968075514 -0.0030872984789311886 -0.0018644541269168258 -0.0006150574772618711; -0.009383576922118664 -0.008116221986711025 -0.006870362441986799 -0.0056122238747775555 -0.004373305477201939 -0.0031349461060017347 -0.0018945677438750863 -0.000629731803201139; -0.009383318945765495 -0.008127840235829353 -0.0068727401085197926 -0.005620555952191353 -0.004364602267742157 -0.0031166167464107275 -0.0018554575508460402 -0.0006122469785623252; -0.009387199766933918 -0.008142982609570026 -0.006869061384350061 -0.005659373011440039 -0.004346032161265612 -0.0031334366649389267 -0.0019081718055531383 -0.0006303898408077657; -0.007493356708437204 -0.006264030002057552 -0.005002946127206087 -0.0037581848446279764 -0.002489660866558552 -0.001259740674868226 5.822963885293575e-6 0.0012671990552917123; -0.005433131940662861 -0.0041743251495063305 -0.0029151486232876778 -0.0016587673453614116 -0.0004036824684590101 0.0008498518145643175 0.0021015105303376913 0.003311693202704191; -0.0033294230233877897 -0.0021215269807726145 -0.0008218666189350188 0.00041033868910744786 0.001676113111898303 0.0029267892241477966 0.004166437778621912 0.005427201744168997; -0.0012400695122778416 1.6189262169064023e-5 0.0012610518606379628 0.002499060472473502 0.0037334291264414787 0.005010172259062529 0.006243477109819651 0.007489427924156189; 0.0006174595328047872 0.0018570702522993088 0.003139302134513855 0.004388014320284128 0.005602084565907717 0.006885211449116468 0.008115818724036217 0.009366238489747047; 0.0006449964712373912 0.0018753943732008338 0.003131445962935686 0.004400795325636864 0.005629609804600477 0.006858657114207745 0.008121082559227943 0.009369242936372757; 0.0006369430921040475 0.0018770081223919988 0.003134477650746703 0.004396272823214531 0.005629359744489193 0.006885003298521042 0.008149908855557442 0.009353983215987682; 0.0006153813446871936 0.001855590962804854 0.0031470570247620344 0.004388659261167049 0.005638826638460159 0.006877109408378601 0.008112108334898949 0.009357183240354061; 0.000624562322627753 0.0018861608114093542 0.0031195098999887705 0.004342163912951946 0.0056305089965462685 0.006884912960231304 0.008129630237817764 0.009384354576468468; 0.0006523219053633511 0.0018763619009405375 0.0031505885999649763 0.004387507680803537 0.005619525443762541 0.006843802984803915 0.008125513792037964 0.009350545704364777; 0.0006048553623259068 0.0018649137346073985 0.003101593814790249 0.004370865877717733 0.0056093595921993256 0.006860679015517235 0.00812781322747469 0.009348893538117409; 0.0006240986404009163 0.0018683240050449967 0.0031189864967018366 0.004382333252578974 0.005600797478109598 0.0068647791631519794 0.008138845674693584 0.00937311165034771; 0.0006317986990325153 0.0018778267549350858 0.0031436157878488302 0.00439099594950676 0.005633593071252108 0.006846386473625898 0.008136685006320477 0.0093848230317235; 0.000603783642873168 0.001904472359456122 0.003132951445877552 0.0043963659554719925 0.005609956104308367 0.006884818896651268 0.008123313076794147 0.009370837360620499; 0.0006194633315317333 0.0018262512749060988 0.0031488698441535234 0.004359905142337084 0.00560968741774559 0.006889741867780685 0.008123478852212429 0.009380241855978966; 0.0006362473941408098 0.0018725405680015683 0.0031487441156059504 0.004355702083557844 0.0056257955729961395 0.006885551381856203 0.008117509074509144 0.00936619658023119; 0.0006146921077743173 0.0018414710648357868 0.0031098921317607164 0.0043634409084916115 0.005639617331326008 0.006866533774882555 0.008117900229990482 0.009367567487061024; 0.0006125420331954956 0.001858829171396792 0.0031372574158012867 0.004350574687123299 0.005622847471386194 0.006874744780361652 0.00812161061912775 0.009379812516272068; 0.000619733240455389 0.0018467928748577833 0.0031116183381527662 0.004365162458270788 0.005617384798824787 0.006876361556351185 0.008127051405608654 0.009362704120576382; 0.0006377476383931935 0.0018767534056678414 0.003138098632916808 0.0043703606352210045 0.005625295452773571 0.006887627765536308 0.00814059842377901 0.009356828406453133; 0.0006170943379402161 0.0018555864226073027 0.0031311186030507088 0.0043739210814237595 0.0056072017177939415 0.006884769536554813 0.008120864629745483 0.009368285536766052; 0.0006326670409180224 0.0018904830794781446 0.0031142886728048325 0.004369840491563082 0.005644507240504026 0.006852438673377037 0.008123869076371193 0.009372341446578503; 0.0006305003189481795 0.0018717283383011818 0.003121057292446494 0.004350634291768074 0.005623634438961744 0.006879564840346575 0.008125691674649715 0.009382423013448715; 0.0006224439130164683 0.0018909755162894726 0.0031182027887552977 0.004400548990815878 0.00563489506021142 0.006888897158205509 0.008137620985507965 0.00938192754983902; 0.0006321687833406031 0.0018648511031642556 0.003113007638603449 0.004356454126536846 0.005598168820142746 0.00686328811571002 0.008145874366164207 0.00937619712203741; 0.0006288984441198409 0.0018624098738655448 0.003112382721155882 0.004375729244202375 0.005629426799714565 0.006861936300992966 0.00813478883355856 0.009387928061187267])
and then build our plot:
hm = heatmap!(axb, xb, yb, b_top, colorrange=(0, Δb), colormap=:thermal)
Colorbar(fig[1, 1], hm, flipaxis=false, label="Surface b(x, y) (m s⁻²)")
hm = heatmap!(axζ, xζ, yζ, ζ_top, colorrange=(-5e-5, 5e-5), colormap=:balance)
Colorbar(fig[1, 4], hm, label="Surface ζ(x, y) (s⁻¹)")
hm = heatmap!(axu, yb, zb, U; colorrange=(-5e-1, 5e-1), colormap=:balance)
Colorbar(fig[2, 1], hm, flipaxis=false, label="Zonally-averaged U(y, z) (m s⁻¹)")
contour!(axu, yb, zb, B; levels=15, color=:black)
hm = heatmap!(axv, yv, zb, V; colorrange=(-1e-1, 1e-1), colormap=:balance)
Colorbar(fig[2, 4], hm, label="Zonally-averaged V(y, z) (m s⁻¹)")
contour!(axv, yb, zb, B; levels=15, color=:black)
Finally, we're ready to record the movie.
frames = 1:length(times)
record(fig, filename * ".mp4", frames, framerate=8) do i
n[] = i
end
This page was generated using Literate.jl.