Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Double gyre example #1085

Closed
wants to merge 54 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
d29edfe
first attempt for a double gyre example; some instability rises
navidcy Aug 4, 2020
9340ce3
change buoyancy forcing to flux BC
navidcy Aug 7, 2020
65e8289
this seems like a working double-gyre but blows up eventually
navidcy Aug 7, 2020
ba83dfe
Merge branch 'master' into double-gyre
navidcy Sep 4, 2020
4822dfa
adds biharmonic; BC's don't work
navidcy Sep 5, 2020
75b467e
now example includes biharmonic lateral viscosity/diffusivity
navidcy Sep 5, 2020
687b1ae
buoyancy relaxation is a BC, not forcing
navidcy Sep 8, 2020
1a1843d
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Sep 12, 2020
9d0daa7
updates to run on latest Oceanagigans
navidcy Sep 17, 2020
182c111
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Sep 17, 2020
1240eab
some more attempts but still blows up...
navidcy Sep 18, 2020
bd33f3b
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Sep 23, 2020
6dbbca8
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Sep 25, 2020
f69dcf7
updates Manifest
navidcy Sep 25, 2020
29b7ca0
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Sep 30, 2020
7af19b3
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Oct 1, 2020
ce37ca2
yet another version that blows up :(
navidcy Oct 1, 2020
c99fc5e
resolves conflicts
navidcy Oct 20, 2020
14f4406
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Oct 20, 2020
d822c84
Updates double gyre example;
glwagner Oct 28, 2020
330878e
Merge branch 'master' into double-gyre
glwagner Oct 28, 2020
43d713c
Tweaks comments and presentation
glwagner Oct 28, 2020
4e72b15
Smaller time-step for hurricane-strength winds
glwagner Oct 28, 2020
32a06cb
The checkpointer will save us
glwagner Oct 29, 2020
3dbaa23
Merge remote-tracking branch 'upstream/master' into double-gyre
navidcy Mar 14, 2021
e13533a
Merge branch 'double-gyre' of github.com:navidcy/Oceananigans.jl into…
navidcy Mar 14, 2021
a2445e8
Merge branch 'CliMA:master' into double-gyre
navidcy Jul 6, 2021
4c77b1b
Merge branch 'CliMA:master' into double-gyre
navidcy Jul 24, 2021
3ccffba
Merge branch 'CliMA:master' into double-gyre
navidcy Aug 12, 2021
ea0423f
Merge branch 'CliMA:master' into double-gyre
navidcy Sep 12, 2021
5fa8ff5
update double_gyre script to latest Oceananigans shenanigans
navidcy Sep 12, 2021
162bc2f
remove nonhydrostaticmodel [skip ci]
navidcy Sep 12, 2021
c5b35a8
some tidying up [skip ci]
navidcy Sep 12, 2021
46c6e1f
some updates [skip ci]
navidcy Sep 14, 2021
dd83f75
Merge branch 'CliMA:main' into double-gyre
navidcy Dec 2, 2021
2f8c24c
updates double gyre script
navidcy Dec 2, 2021
cb38d07
fix deprecation in time-step wizard
navidcy Dec 7, 2021
0e10c11
Merge branch 'main' of https://github.com/CliMA/Oceananigans.jl into …
navidcy Jan 26, 2022
f581869
updates [skip ci]
navidcy Jan 30, 2022
3296e1b
Merge branch 'main' of https://github.com/CliMA/Oceananigans.jl into …
navidcy Jan 30, 2022
d6c1956
adds double gyre
navidcy Jan 30, 2022
949fd45
barotropic streamfunction plot
navidcy Jan 31, 2022
67e2c39
resolve merge conflict
navidcy Apr 21, 2022
4f7a4da
resolve merge conflict
navidcy Apr 21, 2022
d025f3d
Merge branch 'CliMA:main' into double-gyre
navidcy Apr 25, 2022
a2b2588
Merge branch 'CliMA:main' into double-gyre
navidcy May 16, 2022
c4a92b2
Merge branch 'CliMA:main' into double-gyre
navidcy May 24, 2022
07c919a
Merge branch 'main' into double-gyre
ali-ramadhan Oct 3, 2022
ca621da
Update double gyre script
ali-ramadhan Oct 3, 2022
d0c06eb
Woops
ali-ramadhan Oct 3, 2022
00c2a69
simplify bc and forcing
navidcy Oct 11, 2022
023b388
use CairoMakie
navidcy Oct 12, 2022
6142331
Merge branch 'CliMA:main' into double-gyre
navidcy Oct 30, 2022
94fdef6
Merge branch 'CliMA:main' into double-gyre
navidcy Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ examples = [
"convecting_plankton.jl",
"ocean_wind_mixing_and_convection.jl",
"langmuir_turbulence.jl",
"double_gyre.jl",
"baroclinic_adjustment.jl",
"kelvin_helmholtz_instability.jl",
"shallow_water_Bickley_jet.jl",
Expand All @@ -58,6 +59,7 @@ example_pages = [
"Convecting plankton" => "generated/convecting_plankton.md",
"Ocean wind mixing and convection" => "generated/ocean_wind_mixing_and_convection.md",
"Langmuir turbulence" => "generated/langmuir_turbulence.md",
"Double gyre" => "generated/double_gyre.md",
"Baroclinic adjustment" => "generated/baroclinic_adjustment.md",
"Kelvin-Helmholtz instability" => "generated/kelvin_helmholtz_instability.md",
"Shallow water Bickley jet" => "generated/shallow_water_Bickley_jet.md",
Expand Down
285 changes: 285 additions & 0 deletions examples/double_gyre.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
# # Double Gyre
#
# This example simulates a double gyre following:
# https://mitgcm.readthedocs.io/en/latest/examples/baroclinic_gyre/baroclinic_gyre.html

using Oceananigans
using Oceananigans.Units
using Oceananigans.Grids: xnode, ynode, znode
using Oceananigans.TurbulenceClosures: VerticallyImplicitTimeDiscretization
using CairoMakie
using Printf

const Lx = 4000kilometers # east-west extent [m]
const Ly = 6000kilometers # north-south extent [m]
const Lz = 1.8kilometers # depth [m]

Δt₀ = 10minutes
stop_time = 10years

Nx = 160
Ny = 240
Nz = 50

σ = 1.2 # stretching factor
hyperbolically_spaced_faces(k) = - Lz * (1 - tanh(σ * (k - 1) / Nz) / tanh(σ))

grid = RectilinearGrid(CPU();
size = (Nx, Ny, Nz),
x = (-Lx/2, Lx/2),
y = (-Ly/2, Ly/2),
z = hyperbolically_spaced_faces,
topology = (Bounded, Bounded, Bounded))

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

fig = Figure(resolution=(1200, 800))
ax = Axis(fig[1, 1], ylabel = "Depth (m)", xlabel = "Vertical spacing (m)")
lines!(ax, grid.Δzᵃᵃᶜ[1:grid.Nz], grid.zᵃᵃᶜ[1:grid.Nz])
scatter!(ax, grid.Δzᵃᵃᶜ[1:Nz], grid.zᵃᵃᶜ[1:Nz])

save("double_gyre_grid_spacing.svg", fig)
nothing #hide

# ![](double_gyre_grid_spacing.svg)

α = 2e-4 # [K⁻¹] thermal expansion coefficient
g = 9.81 # [m s⁻²] gravitational constant
cᵖ = 3991 # [J K⁻¹ kg⁻¹] heat capacity for seawater
ρ₀ = 1028 # [kg m⁻³] reference density

parameters = (Ly = Ly,
Lz = Lz,
τ = 0.1 / ρ₀, # surface kinematic wind stress [m² s⁻²]
μ = 1 / 30days, # bottom drag damping time-scale [s⁻¹]
Δb = 30 * α * g, # surface vertical buoyancy gradient [s⁻²]
λ = 30days # relaxation time scale [s]
)

# ## Boundary conditions
#
# ### Wind stress

@inline u_stress(x, y, t, p) = - p.τ * cos(2π * y / p.Ly)
u_stress_bc = FluxBoundaryCondition(u_stress; parameters)

# ### Bottom drag
@inline u_drag(x, y, t, u, p) = - p.μ * p.Lz * u
@inline v_drag(x, y, t, v, p) = - p.μ * p.Lz * v

u_drag_bc = FluxBoundaryCondition(u_drag; field_dependencies=:u, parameters)
v_drag_bc = FluxBoundaryCondition(v_drag; field_dependencies=:v, parameters)

u_bcs = FieldBoundaryConditions(top = u_stress_bc, bottom = u_drag_bc)
v_bcs = FieldBoundaryConditions( bottom = v_drag_bc)


# ### Buoyancy relaxation

@inline buoyancy_relaxation(x, y, z, t, b, p) = - 1 / p.λ * (b - p.Δb * y / p.Ly)
Fb = Forcing(buoyancy_relaxation; parameters, field_dependencies=:b)

# ## Turbulence closure
horizontal_diffusive_closure = HorizontalScalarDiffusivity(ν=5000, κ=1000)
vertical_diffusive_closure = VerticalScalarDiffusivity(VerticallyImplicitTimeDiscretization(),
ν=1e-2, κ=1e-5)

# ## Model building

model = HydrostaticFreeSurfaceModel(; grid,
free_surface = ImplicitFreeSurface(),
momentum_advection = WENO(),
tracer_advection = WENO(),
buoyancy = BuoyancyTracer(),
coriolis = BetaPlane(latitude=45),
closure = (vertical_diffusive_closure, horizontal_diffusive_closure),
tracers = :b,
boundary_conditions = (u=u_bcs, v=v_bcs),
forcing = (b=Fb,)
)

# ## Initial conditions

bᵢ(x, y, z) = parameters.Δb * (1 + z / grid.Lz)

set!(model, b=bᵢ)

# ## Simulation setup

simulation = Simulation(model, Δt=Δt₀, stop_time=stop_time)

# add timestep wizard callback
max_Δt = 1 / 5model.coriolis.f₀
wizard = TimeStepWizard(cfl=0.15, max_change=1.1, max_Δt=max_Δt)

simulation.callbacks[:wizard] = Callback(wizard, IterationInterval(20))

# add progress callback
wall_clock = [time_ns()]

function print_progress(sim)
@printf("[%05.2f%%] i: %d, t: %s, wall time: %s, max(u): (%6.3e, %6.3e, %6.3e) m/s, next Δt: %s\n",
100 * (sim.model.clock.time / sim.stop_time),
sim.model.clock.iteration,
prettytime(sim.model.clock.time),
prettytime(1e-9 * (time_ns() - wall_clock[1])),
maximum(abs, sim.model.velocities.u),
maximum(abs, sim.model.velocities.v),
maximum(abs, sim.model.velocities.w),
prettytime(sim.Δt))

wall_clock[1] = time_ns()

return nothing
end

simulation.callbacks[:print_progress] = Callback(print_progress, IterationInterval(200))


# ## Output

u, v, w = model.velocities
b = model.tracers.b

speed = Field(u^2 + v^2)
buoyancy_variance = Field(b^2)

outputs = merge(model.velocities, model.tracers, (speed=speed, b²=buoyancy_variance))

simulation.output_writers[:fields] = JLD2OutputWriter(model, outputs,
schedule = TimeInterval(7days),
filename = "double_gyre",
indices = (:, :, model.grid.Nz),
overwrite_existing = true)

barotropic_u = Field(Average(model.velocities.u, dims=3))
barotropic_v = Field(Average(model.velocities.v, dims=3))

simulation.output_writers[:barotropic_velocities] =
JLD2OutputWriter(model, (u=barotropic_u, v=barotropic_v),
schedule = AveragedTimeInterval(30days, window=10days),
filename = "double_gyre_circulation",
overwrite_existing = true)

run!(simulation)

# # A neat movie

# We open the JLD2 file, and extract the `grid` and the iterations we ended up saving at,

using JLD2

filename = "double_gyre.jld2"

u_timeseries = FieldTimeSeries(filename, "u"; grid = grid)
v_timeseries = FieldTimeSeries(filename, "v"; grid = grid)
s_timeseries = FieldTimeSeries(filename, "speed"; grid = grid)

times = u_timeseries.times

xu, yu, zu = nodes(u_timeseries[1])
xv, yv, zv = nodes(v_timeseries[1])
xs, ys, zs = nodes(s_timeseries[1])

xlims = (-grid.Lx/2 * 1e-3, grid.Lx/2 * 1e-3)
ylims = (-grid.Ly/2 * 1e-3, grid.Ly/2 * 1e-3)

xu_km, yu_km = xu * 1e-3, yu * 1e-3
xv_km, yv_km = xv * 1e-3, yv * 1e-3
xs_km, ys_km = xs * 1e-3, ys * 1e-3

# These utilities are handy for calculating nice contour intervals:

""" Returns colorbar levels equispaced from `(-clim, clim)` and encompassing the extrema of `c`. """
function divergent_levels(c, clim, nlevels=21)
levels = range(-clim, stop=clim, length=nlevels)
cmax = maximum(abs, c)
return ((-clim, clim), clim > cmax ? levels : levels = vcat([-cmax], levels, [cmax]))
end

""" Returns colorbar levels equispaced between `clims` and encompassing the extrema of `c`."""
function sequential_levels(c, clims, nlevels=20)
levels = range(clims[1], stop=clims[2], length=nlevels)
cmin, cmax = minimum(c), maximum(c)
cmin < clims[1] && (levels = vcat([cmin], levels))
cmax > clims[2] && (levels = vcat(levels, [cmax]))
return clims, levels
end

# Finally, we're ready to animate.

@info "Making an animation from the saved data..."

anim = @animate for i in 1:length(times)

@info "Drawing frame $i from iteration $(length(times)) \n"

t = times[i]
u = interior(u_timeseries[i])[:, :, grid.Nz]
v = interior(v_timeseries[i])[:, :, grid.Nz]
s = interior(s_timeseries[i])[:, :, grid.Nz]

ulims, ulevels = divergent_levels(u, 0.8)
slims, slevels = sequential_levels(s, (0.0, 0.8))

kwargs = (aspectratio=:equal, linewidth=0, xlims=xlims,
ylims=ylims, xlabel="x (km)", ylabel="y (km)")

u_plot = contourf(xu_km, yu_km, u';
color = :balance,
clims = ulims,
levels = ulevels,
kwargs...)

v_plot = contourf(xv_km, yv_km, v';
color = :balance,
clims = ulims,
levels = ulevels,
kwargs...)

s_plot = contourf(xs_km, ys_km, s';
color = :thermal,
clims = slims,
levels = slevels,
kwargs...)

plot(u_plot, v_plot, s_plot, layout = Plots.grid(1, 3), size=(1200, 500),
title = ["u(t="*string(round(t/day, digits=1))*" day)" "speed"])
end

mp4(anim, "double_gyre.mp4", fps = 8) # hide

# Plot the barotropic circulation

filename_barotropic = "double_gyre_circulation.jld2"

U_timeseries = FieldTimeSeries(filename_barotropic, "u"; grid = grid)
V_timeseries = FieldTimeSeries(filename_barotropic, "v"; grid = grid)

# average for the last `n_years`
n_years = 5

using Statistics

U = mean(interior(U_timeseries)[:, :, :, end-n_years*12:end], dims=4)[:, :, 1, 1]
V = mean(interior(V_timeseries)[:, :, :, end-n_years*12:end], dims=4)[:, :, 1, 1]

U_plot = contourf(xu_km, yu_km, U', xlims=xlims, ylims=ylims,
linewidth=0, color=:balance, aspectratio=:equal)

V_plot = contourf(xv_km, yv_km, V', xlims=xlims, ylims=ylims,
linewidth=0, color=:balance, aspectratio=:equal)

Ψ = cumsum(V, dims=1) * grid.Δxᶜᵃᵃ * grid.Lz * 1e-6

Ψlims, Ψlevels = divergent_levels(Ψ, 45)

Ψ_plot = contourf(xv_km, yv_km, Ψ', xlims=xlims, ylims=ylims, levels = Ψlevels, clims = Ψlims,
linewidth=0, color=:balance, aspectratio=:equal)

plot(U_plot, V_plot, Ψ_plot, layout = Plots.grid(1, 3), size=(1200, 400),
title=["Depth- and time-averaged \$ u \$" "Depth- and time-averaged \$ v \$" "Barotropic streamfunction"])

savefig("double_gyre_circulation.png"); nothing # hide

![](assets/double_gyre_circulation.svg)