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.UnitsGrid
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.0Model
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⁻²]")
figSimulation
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
├── run_wall_time: 0 seconds
├── run_wall_time / 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 entriesWe 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 KiBNow 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: 19.534 seconds, max(u): (0.000e+00, 0.000e+00, 0.000e+00) m/s, next Δt: 20 minutes
[ Info: ... simulation initialization complete (13.764 seconds)
[ Info: Executing initial time step...
[ Info: ... initial time step complete (9.872 seconds).
[06.94%] i: 100, t: 1.389 days, wall time: 20.932 seconds, max(u): (1.275e-01, 1.318e-01, 1.609e-03) m/s, next Δt: 20 minutes
[13.89%] i: 200, t: 2.778 days, wall time: 1.863 seconds, max(u): (2.199e-01, 1.860e-01, 1.791e-03) m/s, next Δt: 20 minutes
[20.83%] i: 300, t: 4.167 days, wall time: 1.733 seconds, max(u): (2.845e-01, 2.423e-01, 1.864e-03) m/s, next Δt: 20 minutes
[27.78%] i: 400, t: 5.556 days, wall time: 1.729 seconds, max(u): (3.474e-01, 3.176e-01, 2.013e-03) m/s, next Δt: 20 minutes
[34.72%] i: 500, t: 6.944 days, wall time: 1.811 seconds, max(u): (4.188e-01, 4.262e-01, 2.000e-03) m/s, next Δt: 20 minutes
[41.67%] i: 600, t: 8.333 days, wall time: 1.745 seconds, max(u): (5.230e-01, 5.565e-01, 2.239e-03) m/s, next Δt: 20 minutes
[48.61%] i: 700, t: 9.722 days, wall time: 1.826 seconds, max(u): (6.861e-01, 9.935e-01, 2.724e-03) m/s, next Δt: 20 minutes
[55.56%] i: 800, t: 11.111 days, wall time: 1.767 seconds, max(u): (1.112e+00, 1.164e+00, 4.409e-03) m/s, next Δt: 20 minutes
[62.50%] i: 900, t: 12.500 days, wall time: 1.824 seconds, max(u): (1.414e+00, 1.262e+00, 4.736e-03) m/s, next Δt: 20 minutes
[69.44%] i: 1000, t: 13.889 days, wall time: 1.732 seconds, max(u): (1.390e+00, 1.192e+00, 3.806e-03) m/s, next Δt: 20 minutes
[76.39%] i: 1100, t: 15.278 days, wall time: 1.826 seconds, max(u): (1.292e+00, 1.107e+00, 4.500e-03) m/s, next Δt: 20 minutes
[83.33%] i: 1200, t: 16.667 days, wall time: 1.702 seconds, max(u): (1.237e+00, 1.091e+00, 3.753e-03) m/s, next Δt: 20 minutes
[90.28%] i: 1300, t: 18.056 days, wall time: 1.716 seconds, max(u): (1.244e+00, 1.168e+00, 2.908e-03) m/s, next Δt: 20 minutes
[97.22%] i: 1400, t: 19.444 days, wall time: 1.938 seconds, max(u): (1.306e+00, 1.039e+00, 3.556e-03) m/s, next Δt: 20 minutes
[ Info: Simulation is stopping after running for 51.930 seconds.
[ Info: Simulation time 20 days equals or exceeds stop time 20 days.
[ Info: Simulation completed in 51.950 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 CairoMakieThree-dimensional visualization
We load the saved buoyancy output on the top, north, and east surface as FieldTimeSerieses.
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.grid48×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.0We 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 Observables, check out Makie.jl's Documentation.
n = length(times)41Now 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.0Next, 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.009399231523275375 -0.008118770085275173 -0.006890648044645786 -0.005642762407660484 -0.00438172509893775 -0.0031486807856708765 -0.0018852592911571264 -0.000662265985738486; -0.00937830749899149 -0.00811366643756628 -0.00690114451572299 -0.0055956714786589146 -0.0044051227159798145 -0.0031342599540948868 -0.0018786629661917686 -0.000639432983007282; -0.009400267153978348 -0.008111866191029549 -0.006867287680506706 -0.005633074790239334 -0.004391221329569817 -0.0031072585843503475 -0.001865973463281989 -0.000636295648291707; -0.009353994391858578 -0.008143560029566288 -0.006880394648760557 -0.005610945168882608 -0.004373765550553799 -0.003126329742372036 -0.0018557750154286623 -0.0006460310542024672; -0.009378854185342789 -0.008130119182169437 -0.006872739642858505 -0.005614057183265686 -0.004391612485051155 -0.003141530090942979 -0.0018825209699571133 -0.0006193348090164363; -0.009398065507411957 -0.008113013580441475 -0.006868585012853146 -0.005617435555905104 -0.004379602614790201 -0.0031440688762813807 -0.001901495736092329 -0.0006186996470205486; -0.00938210915774107 -0.00812756922096014 -0.00686601409688592 -0.0055969757959246635 -0.00438758684322238 -0.0031301521230489016 -0.0018813821952790022 -0.0006343861459754407; -0.009373322129249573 -0.008127043955028057 -0.006893862970173359 -0.005616901908069849 -0.004377820994704962 -0.0031106637325137854 -0.0018514251569285989 -0.0006305914139375091; -0.009365182369947433 -0.008132393471896648 -0.006871682591736317 -0.005614112131297588 -0.004382030572742224 -0.0031311807688325644 -0.0018718797946348786 -0.0006031938828527927; -0.009370356798171997 -0.008102507330477238 -0.006872872821986675 -0.005610753316432238 -0.004354320000857115 -0.003113596932962537 -0.0018630973063409328 -0.0006329375319182873; -0.009362278506159782 -0.008128384128212929 -0.006904384586960077 -0.005613713525235653 -0.004387137945741415 -0.00313279265537858 -0.0018847817555069923 -0.0006208523409441113; -0.009402441792190075 -0.008139989338815212 -0.006859421730041504 -0.005632802378386259 -0.004392556846141815 -0.0031134525779634714 -0.001885074540041387 -0.0006103084306232631; -0.009391282685101032 -0.008102180436253548 -0.0068850102834403515 -0.005632920190691948 -0.004352625459432602 -0.00315820868127048 -0.00186941958963871 -0.0006280390080064535; -0.009367171674966812 -0.008106153458356857 -0.006891411729156971 -0.005619746632874012 -0.004386917222291231 -0.0031297500245273113 -0.0018756914651021361 -0.0006060960586182773; -0.009372229687869549 -0.008096461184322834 -0.006876865867525339 -0.005634549539536238 -0.004391124472022057 -0.0031052404083311558 -0.0018586601363494992 -0.0006185526144690812; -0.009387553669512272 -0.008121805265545845 -0.006845470983535051 -0.0056227282620966434 -0.004384741187095642 -0.0031054909341037273 -0.0018700379878282547 -0.0006177630857564509; -0.009382544085383415 -0.00809940230101347 -0.0068914154544472694 -0.0056357961148023605 -0.004363782238215208 -0.0031245367135852575 -0.0018786797299981117 -0.0006445685285143554; -0.00936688482761383 -0.008119073696434498 -0.006872775498777628 -0.005659540183842182 -0.004407528787851334 -0.003111260011792183 -0.0018718689680099487 -0.0006338324747048318; -0.009336566552519798 -0.008092941716313362 -0.006886683404445648 -0.005626440513879061 -0.004359652753919363 -0.003139937063679099 -0.0019022984197363257 -0.0006062762695364654; -0.009372980333864689 -0.008149588480591774 -0.006908721290528774 -0.005646322388201952 -0.004359515383839607 -0.003135023405775428 -0.0018550176173448563 -0.0006124385399743915; -0.009399089962244034 -0.008128250017762184 -0.006865258794277906 -0.005613213405013084 -0.004369678441435099 -0.003104736329987645 -0.0019021942280232906 -0.0006376604433171451; -0.009383110329508781 -0.008115097880363464 -0.006854452658444643 -0.005632350686937571 -0.004363203886896372 -0.003148708026856184 -0.0018786179134622216 -0.0006102303741499782; -0.0074886842630803585 -0.006245553959161043 -0.005005158483982086 -0.0037699141539633274 -0.0024953458923846483 -0.0012819630792364478 3.879434189002495e-6 0.0012349041644483805; -0.005407597403973341 -0.004190078005194664 -0.0028819646686315536 -0.0016369493678212166 -0.00037380127469077706 0.0008168963831849396 0.0020846426486968994 0.003325502621009946; -0.0033336044289171696 -0.002087648492306471 -0.0008334254380315542 0.0004312782548367977 0.001699899323284626 0.002905898028984666 0.004163762088865042 0.0054069990292191505; -0.0012515156995505095 4.365255790617084e-6 0.001241044607013464 0.0024987205397337675 0.0037595676258206367 0.005001890938729048 0.006262710317969322 0.007503692992031574; 0.0006055985577404499 0.0018800761317834258 0.0031346650794148445 0.0043966579250991344 0.005646122619509697 0.00685895886272192 0.00811830349266529 0.009355532936751842; 0.0006062597967684269 0.0018873164663091302 0.0031399293802678585 0.004345934838056564 0.005631438456475735 0.006837211549282074 0.008104154840111732 0.009386125952005386; 0.0006237562047317624 0.0018651061691343784 0.00313135189935565 0.004371243063360453 0.005631748586893082 0.0068993838503956795 0.008106768131256104 0.009365267120301723; 0.0005873879999853671 0.0018587546655908227 0.003119579516351223 0.0043648318387568 0.005626224912703037 0.00686415983363986 0.008119363337755203 0.009374845772981644; 0.0006081237806938589 0.0018874092493206263 0.0031065295916050673 0.004365222994238138 0.005609342362731695 0.006892242468893528 0.00812594685703516 0.009376594796776772; 0.0006347932503558695 0.0018748336005955935 0.003147819545120001 0.004374758806079626 0.005630783271044493 0.006877236533910036 0.008144330233335495 0.009351488202810287; 0.000629120972007513 0.001887966995127499 0.003103004302829504 0.004361994564533234 0.005605428945273161 0.0068689268082380295 0.008131404407322407 0.009369508363306522; 0.0006173313013277948 0.0018508514622226357 0.003134295577183366 0.004377017263323069 0.0056039122864604 0.00688224658370018 0.008122777566313744 0.009378946386277676; 0.0006160478806123137 0.001884399214759469 0.003143234411254525 0.004398648627102375 0.005644478835165501 0.006839963141828775 0.00814041681587696 0.009371759369969368; 0.0006325143622234464 0.0018539319280534983 0.0031328070908784866 0.004362184554338455 0.005605907179415226 0.006878745276480913 0.008103950880467892 0.00939133856445551; 0.000623050145804882 0.0018807546002790332 0.003139721928164363 0.004391358699649572 0.005644921213388443 0.006869573146104813 0.008115782402455807 0.009386955760419369; 0.0006256356718949974 0.0019074974115937948 0.0031191131565719843 0.004372254014015198 0.005638464819639921 0.006859705317765474 0.008124636486172676 0.009372650645673275; 0.0006368079921230674 0.0018872551154345274 0.0031191271264106035 0.004378829151391983 0.005597893614321947 0.00687427818775177 0.008106081746518612 0.009373447857797146; 0.0006212848820723593 0.001889502047561109 0.0031139820348471403 0.004388323985040188 0.0056063588708639145 0.006895758211612701 0.008121960796415806 0.00936616025865078; 0.0006286309217102826 0.0018883341690525413 0.003123332280665636 0.00433366047218442 0.005636679474264383 0.006908108945935965 0.008107451722025871 0.00937877967953682; 0.0006277633365243673 0.0018705325201153755 0.003138582454994321 0.004377405159175396 0.0056131635792553425 0.006881341803818941 0.008121347054839134 0.009375154040753841; 0.0006273270701058209 0.0018675089813768864 0.0031473138369619846 0.004371547140181065 0.005590206943452358 0.006876189727336168 0.008121975697577 0.009370284155011177; 0.000634560885373503 0.0018677299376577139 0.0031380155123770237 0.004373976495116949 0.005616557784378529 0.006878519430756569 0.008126611821353436 0.00941476784646511; 0.0006099623278714716 0.0018853240180760622 0.0031025244388729334 0.004398017190396786 0.005643169395625591 0.006852428428828716 0.00811745971441269 0.00936217326670885; 0.0006217245245352387 0.0018644829979166389 0.0031389761716127396 0.004406994674354792 0.0056228600442409515 0.006864776834845543 0.008142070844769478 0.009370839223265648; 0.0006331648328341544 0.0018776499200612307 0.003133327467367053 0.004367020912468433 0.005620533134788275 0.006884723901748657 0.008119508624076843 0.009370704181492329; 0.0006225631223060191 0.0018596518784761429 0.0031383538153022528 0.004382015205919743 0.005627131555229425 0.006863579619675875 0.008123388513922691 0.009364476427435875])
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
endThis page was generated using Literate.jl.