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{3, Float64, Float32}(order=5)
│ └── b: WENO{3, Float64, Float32}(order=5)
├── vertical_coordinate: ZCoordinate
└── 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
set_theme!(Theme(fontsize = 20))
# 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 => 4
│ ├── stop_iteration_exceeded => -
│ ├── wall_time_limit_exceeded => e
│ └── nan_checker => }
├── 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: 36.839 seconds, max(u): (0.000e+00, 0.000e+00, 0.000e+00) m/s, next Δt: 20 minutes
[ Info: ... simulation initialization complete (32.009 seconds)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (24.306 seconds).
[06.94%] i: 100, t: 1.389 days, wall time: 48.969 seconds, max(u): (1.254e-01, 1.125e-01, 1.590e-03) m/s, next Δt: 20 minutes
[13.89%] i: 200, t: 2.778 days, wall time: 917.888 ms, max(u): (2.125e-01, 1.760e-01, 1.756e-03) m/s, next Δt: 20 minutes
[20.83%] i: 300, t: 4.167 days, wall time: 758.120 ms, max(u): (3.071e-01, 2.604e-01, 1.807e-03) m/s, next Δt: 20 minutes
[27.78%] i: 400, t: 5.556 days, wall time: 777.124 ms, max(u): (3.879e-01, 4.400e-01, 2.008e-03) m/s, next Δt: 20 minutes
[34.72%] i: 500, t: 6.944 days, wall time: 711.076 ms, max(u): (5.077e-01, 6.634e-01, 2.082e-03) m/s, next Δt: 20 minutes
[41.67%] i: 600, t: 8.333 days, wall time: 769.489 ms, max(u): (6.451e-01, 9.940e-01, 2.754e-03) m/s, next Δt: 20 minutes
[48.61%] i: 700, t: 9.722 days, wall time: 805.409 ms, max(u): (1.015e+00, 1.191e+00, 4.040e-03) m/s, next Δt: 20 minutes
[55.56%] i: 800, t: 11.111 days, wall time: 787.032 ms, max(u): (1.244e+00, 1.132e+00, 4.079e-03) m/s, next Δt: 20 minutes
[62.50%] i: 900, t: 12.500 days, wall time: 726.975 ms, max(u): (1.212e+00, 1.240e+00, 4.240e-03) m/s, next Δt: 20 minutes
[69.44%] i: 1000, t: 13.889 days, wall time: 733.764 ms, max(u): (1.311e+00, 1.106e+00, 4.521e-03) m/s, next Δt: 20 minutes
[76.39%] i: 1100, t: 15.278 days, wall time: 831.121 ms, max(u): (1.362e+00, 1.235e+00, 4.176e-03) m/s, next Δt: 20 minutes
[83.33%] i: 1200, t: 16.667 days, wall time: 716.467 ms, max(u): (1.276e+00, 1.079e+00, 3.250e-03) m/s, next Δt: 20 minutes
[90.28%] i: 1300, t: 18.056 days, wall time: 727.607 ms, max(u): (1.345e+00, 1.050e+00, 3.228e-03) m/s, next Δt: 20 minutes
[97.22%] i: 1400, t: 19.444 days, wall time: 731.878 ms, max(u): (1.427e+00, 1.076e+00, 2.743e-03) m/s, next Δt: 20 minutes
[ Info: Simulation is stopping after running for 1.185 minutes.
[ Info: Simulation time 20 days equals or exceeds stop time 20 days.
[ Info: Simulation completed in 1.185 minutes
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
-500.0:20.833333333333332: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!
.
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.009379656985402107 -0.008116493001580238 -0.006849023047834635 -0.005644054617732763 -0.004399443976581097 -0.0031495383009314537 -0.0018486209446564317 -0.0006223110249266028; -0.00938624981790781 -0.008116370998322964 -0.006883829366415739 -0.005632143467664719 -0.004365403670817614 -0.003101746551692486 -0.0018932799575850368 -0.0006419317214749753; -0.009391538798809052 -0.008121037855744362 -0.006882871501147747 -0.0056219627149403095 -0.004359755665063858 -0.0031336680985987186 -0.0018765405984595418 -0.0006241928786039352; -0.009372631087899208 -0.008109480142593384 -0.006855738814920187 -0.005617995280772448 -0.00437938142567873 -0.003128721844404936 -0.001899994327686727 -0.0006290328456088901; -0.009360010735690594 -0.008139324374496937 -0.006864666007459164 -0.005643178708851337 -0.004352445248514414 -0.003123979549854994 -0.0018868506886065006 -0.0006085222703404725; -0.009353810921311378 -0.008099948056042194 -0.006860506255179644 -0.005623454228043556 -0.004377949982881546 -0.0031413694377988577 -0.001884131459519267 -0.0006459923461079597; -0.00937727466225624 -0.008145024999976158 -0.006879596505314112 -0.005612993612885475 -0.004358405247330666 -0.0031102674547582865 -0.001870446838438511 -0.0006074325065128505; -0.009369265288114548 -0.008145464584231377 -0.006868220865726471 -0.005630011670291424 -0.004355300217866898 -0.0031496353913098574 -0.0018692966550588608 -0.0006219206843525171; -0.009360604919493198 -0.00810089148581028 -0.006867611780762672 -0.0056221699342131615 -0.004382207524031401 -0.0031497797463089228 -0.0018599751638248563 -0.0006265395786613226; -0.009383696131408215 -0.008133180439472198 -0.006868807133287191 -0.005624564364552498 -0.004398990422487259 -0.003138649743050337 -0.0018780197715386748 -0.0006293966434895992; -0.009366735816001892 -0.008135705254971981 -0.006879868451505899 -0.005606640595942736 -0.004387392196804285 -0.0031204901169985533 -0.00188569410238415 -0.0006420385907404125; -0.009380011819303036 -0.0081052640452981 -0.006878138054162264 -0.005666024517267942 -0.004365961998701096 -0.0031385072506964207 -0.0018624268705025315 -0.0006406430038623512; -0.009385128505527973 -0.008098645135760307 -0.0068968809209764 -0.005627153441309929 -0.0043662432581186295 -0.003111444180831313 -0.0018801529658958316 -0.0006384834414348006; -0.009368701837956905 -0.008120524697005749 -0.00688899913802743 -0.005641311407089233 -0.0043719131499528885 -0.0031091514974832535 -0.0018913575913757086 -0.0006194692687131464; -0.009362404234707355 -0.008137054741382599 -0.006887694355100393 -0.0056319148279726505 -0.00437232106924057 -0.0031185373663902283 -0.0018949494697153568 -0.0006393780349753797; -0.009375635534524918 -0.008106358349323273 -0.006889995653182268 -0.005611686035990715 -0.004379357676953077 -0.0031328562181442976 -0.001873075496405363 -0.0006215481553226709; -0.009398285299539566 -0.008156792260706425 -0.006853346712887287 -0.00559295155107975 -0.004394080955535173 -0.0031317013781517744 -0.0018975000130012631 -0.0006247510900720954; -0.009385213255882263 -0.008146893233060837 -0.006891937460750341 -0.005632706452161074 -0.00436545442789793 -0.003126890165731311 -0.0018622991628944874 -0.0006352618220262229; -0.00938140507787466 -0.008144701831042767 -0.006871314719319344 -0.00561393890529871 -0.00438354816287756 -0.0031136018224060535 -0.0018756254576146603 -0.0006287164869718254; -0.009361202828586102 -0.008136531338095665 -0.006879460997879505 -0.005628397688269615 -0.004386529326438904 -0.0031494065187871456 -0.0018633852014318109 -0.0006183386431075633; -0.009368588216602802 -0.008121135644614697 -0.006844907999038696 -0.005610199645161629 -0.004362575244158506 -0.0031176090706139803 -0.0018718086648732424 -0.0006120237521827221; -0.00938726682215929 -0.008090054616332054 -0.0068780225701630116 -0.00565215852111578 -0.004364547785371542 -0.003127093194052577 -0.001891594147309661 -0.0006342476117424667; -0.007508108392357826 -0.00627788295969367 -0.004992871079593897 -0.0037450892850756645 -0.002501887734979391 -0.0012459080899134278 2.5301065761595964e-5 0.0012525084894150496; -0.005423801951110363 -0.0041631185449659824 -0.0029326514340937138 -0.0016751768998801708 -0.00041769532253965735 0.0008398609352298081 0.002060375642031431 0.0033714869059622288; -0.0033306009136140347 -0.002078614430502057 -0.0008185163605958223 0.0004302060988266021 0.0016513217706233263 0.002925408771261573 0.004197987727820873 0.005399752873927355; -0.0012546477373689413 -1.0972968993883114e-5 0.0012427314650267363 0.002520195906981826 0.0037556805182248354 0.005020873621106148 0.006235235370695591 0.007496939040720463; 0.0006377312820404768 0.0018621834460645914 0.00309640821069479 0.004363850224763155 0.005626475438475609 0.0068808370269834995 0.008110939525067806 0.009383095428347588; 0.0006556527805514634 0.0018727869028225541 0.003123090136796236 0.0043565453961491585 0.005645107477903366 0.0068708001635968685 0.008131596259772778 0.009387175552546978; 0.0006265459232963622 0.0018713584868237376 0.0031092038843780756 0.004349266644567251 0.005619825329631567 0.0068894196301698685 0.00812490377575159 0.009373809210956097; 0.0006130000110715628 0.0018922367598861456 0.003130367724224925 0.004408368840813637 0.005637901835143566 0.006879743188619614 0.008118880912661552 0.009394440799951553; 0.0006126677035354078 0.0018824589205905795 0.003162829438224435 0.004390648566186428 0.005634596571326256 0.006888356059789658 0.008135398849844933 0.009382716380059719; 0.0006355933728627861 0.0018905705073848367 0.003096427768468857 0.004368646070361137 0.005598618183284998 0.006867652293294668 0.008147510699927807 0.0093846395611763; 0.000625130080152303 0.0018819591496139765 0.0031199029181152582 0.00436300877481699 0.005631763953715563 0.0068953814916312695 0.008144312538206577 0.009356244467198849; 0.0006458169082179666 0.0018671537982299924 0.0031110511627048254 0.004395493306219578 0.005644600372761488 0.00689299963414669 0.00814108271151781 0.009373072534799576; 0.0006202184595167637 0.0018718255450949073 0.003127814270555973 0.004377780016511679 0.005616819951683283 0.006858753971755505 0.008091617375612259 0.009361496195197105; 0.0006217556656338274 0.001864730380475521 0.003120462177321315 0.004381872713565826 0.005601462908089161 0.006866995710879564 0.008134623058140278 0.009389505721628666; 0.0006105158827267587 0.0018665159586817026 0.00311602302826941 0.004364137537777424 0.0056286947801709175 0.006877366453409195 0.008136473596096039 0.009363793767988682; 0.000634571653790772 0.0018846394959837198 0.0031281078699976206 0.004344711545854807 0.005625351797789335 0.006885130889713764 0.008121049962937832 0.00938915740698576; 0.0006052558892406523 0.0018595263827592134 0.003100163070484996 0.00436463812366128 0.0056118168868124485 0.006863554939627647 0.0081255454570055 0.009370479732751846; 0.0006427665357477963 0.0018622259376570582 0.003136449959129095 0.004370393697172403 0.005616874899715185 0.006839150562882423 0.008104071021080017 0.009371988475322723; 0.0006245365948416293 0.0018813185160979629 0.003110663965344429 0.004391439724713564 0.005617136601358652 0.00686022499576211 0.008104675449430943 0.009361056610941887; 0.0006025779875926673 0.0018778133671730757 0.003111140336841345 0.004373450763523579 0.005629943683743477 0.00687727564945817 0.008135163225233555 0.009350850246846676; 0.0006294641643762589 0.0018801818368956447 0.003120405599474907 0.004378343932330608 0.005638104863464832 0.006875141058117151 0.008118431083858013 0.009360788390040398; 0.0005984144518151879 0.0018856730312108994 0.003092401660978794 0.0043844664469361305 0.005603115539997816 0.006874223705381155 0.008128314279019833 0.009364664554595947; 0.0006575541920028627 0.0018879325361922383 0.0031346301548182964 0.004383367951959372 0.005626310594379902 0.006888918112963438 0.008115096017718315 0.009380008094012737; 0.0006310711032710969 0.0018843513680621982 0.0031374043319374323 0.004362912382930517 0.005612864624708891 0.0068762050941586494 0.008134680800139904 0.009362244047224522; 0.0006196029717102647 0.0019009170355275273 0.003124919021502137 0.0043709236197173595 0.0056123556569218636 0.006873779930174351 0.008120954036712646 0.009353975765407085; 0.0006182552315294743 0.0019018088933080435 0.0031255031935870647 0.004375696647912264 0.005622553639113903 0.006872633937746286 0.008108679205179214 0.009377624839544296])
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.