Geostrophic adjustment using Oceananigans.HydrostaticFreeSurfaceModel
This example demonstrates how to simulate the one-dimensional geostrophic adjustment of a free surface using Oceananigans.HydrostaticFreeSurfaceModel. Here, we solve the hydrostatic Boussinesq equations beneath a free surface with a small-amplitude about rest $z = 0$, with boundary conditions expanded around $z = 0$, and free surface dynamics linearized under the assumption $η / H \ll 1$, where $η$ is the free surface displacement, and $H$ is the total depth of the fluid.
Install dependencies
First let's make sure we have all required packages installed.
using Pkg
pkg"add Oceananigans, JLD2, Plots"A one-dimensional domain
We use a one-dimensional domain of geophysical proportions,
using Oceananigans
using Oceananigans.Utils: kilometers
grid = RegularRectilinearGrid(size = (128, 1, 1),
x = (0, 1000kilometers), y = (0, 1), z = (-400, 0),
topology = (Bounded, Periodic, Bounded))RegularRectilinearGrid{Float64, Bounded, Periodic, Bounded}
domain: x ∈ [0.0, 1.0e6], y ∈ [0.0, 1.0], z ∈ [-400.0, 0.0]
topology: (Bounded, Periodic, Bounded)
resolution (Nx, Ny, Nz): (128, 1, 1)
halo size (Hx, Hy, Hz): (1, 1, 1)
grid spacing (Δx, Δy, Δz): (7812.5, 1.0, 400.0)and Coriolis parameter appropriate for the mid-latitudes on Earth,
coriolis = FPlane(f=1e-4)FPlane{Float64}: f = 1.00e-04Building a HydrostaticFreeSurfaceModel
We use grid and coriolis to build a simple HydrostaticFreeSurfaceModel,
using Oceananigans.Models: HydrostaticFreeSurfaceModel
model = HydrostaticFreeSurfaceModel(grid=grid, coriolis=coriolis)HydrostaticFreeSurfaceModel{CPU, Float64}(time = 0 seconds, iteration = 0)
├── grid: RegularRectilinearGrid{Float64, Bounded, Periodic, Bounded}(Nx=128, Ny=1, Nz=1)
├── tracers: (:T, :S)
├── closure: IsotropicDiffusivity{Float64,NamedTuple{(:T, :S),Tuple{Float64,Float64}}}
├── buoyancy: SeawaterBuoyancy{Float64,LinearEquationOfState{Float64},Nothing,Nothing}
└── coriolis: FPlane{Float64}A geostrophic adjustment initial value problem
We pose a geostrophic adjustment problem that consists of a partially-geostrophic Gaussian height field complemented by a geostrophic $y$-velocity,
Gaussian(x, L) = exp(-x^2 / 2L^2)
U = 0.1 # geostrophic velocity
L = grid.Lx / 40 # Gaussian width
x₀ = grid.Lx / 4 # Gaussian center
vᵍ(x, y, z) = - U * (x - x₀) / L * Gaussian(x - x₀, L)
g = model.free_surface.gravitational_acceleration
η₀ = coriolis.f * U * L / g # geostrohpic free surface amplitude
ηᵍ(x, y, z) = η₀ * Gaussian(x - x₀, L)ηᵍ (generic function with 1 method)
We use an initial height field that's twice the geostrophic solution, thus superimposing a geostrophic and ageostrophic component in the free surface displacement field:
ηⁱ(x, y, z) = 2 * ηᵍ(x, y, z)ηⁱ (generic function with 1 method)
We set the initial condition to $vᵍ$ and $ηⁱ$,
set!(model, v=vᵍ, η=ηⁱ)Running a Simulation
We pick a time-step that resolves the surface dynamics,
gravity_wave_speed = sqrt(g * grid.Lz) # hydrostatic (shallow water) gravity wave speed
wave_propagation_time_scale = model.grid.Δx / gravity_wave_speed
simulation = Simulation(model, Δt = 0.1 * wave_propagation_time_scale, stop_iteration = 1000)Simulation{Oceananigans.Models.HydrostaticFreeSurfaceModels.HydrostaticFreeSurfaceModel{CPU, Float64}}
├── Model clock: time = 0 seconds, iteration = 0
├── Next time step (Float64): 12.474 seconds
├── Iteration interval: 1
├── Stop criteria: Any[Oceananigans.Simulations.iteration_limit_exceeded, Oceananigans.Simulations.stop_time_exceeded, Oceananigans.Simulations.wall_time_limit_exceeded]
├── Run time: 0 seconds, wall time limit: Inf
├── Stop time: Inf years, stop iteration: 1000
├── Diagnostics: OrderedCollections.OrderedDict with 1 entry:
│ └── nan_checker => NaNChecker
└── Output writers: OrderedCollections.OrderedDict with no entriesOutput
We output the velocity field and free surface displacement,
output_fields = merge(model.velocities, (η=model.free_surface.η,))
using Oceananigans.OutputWriters: JLD2OutputWriter, IterationInterval
simulation.output_writers[:fields] = JLD2OutputWriter(model, output_fields,
schedule = IterationInterval(10),
prefix = "geostrophic_adjustment",
force = true)
run!(simulation)[ Info: Simulation is stopping. Model iteration 1000 has hit or exceeded simulation stop iteration 1000.
Visualizing the results
using JLD2, Plots, Printf, Oceananigans.Grids
using Oceananigans.Utils: hours
xη = xw = xv = xnodes(model.free_surface.η)
xu = xnodes(model.velocities.u)
file = jldopen(simulation.output_writers[:fields].filepath)
iterations = parse.(Int, keys(file["timeseries/t"]))
anim = @animate for (i, iter) in enumerate(iterations)
u = file["timeseries/u/$iter"][:, 1, 1]
v = file["timeseries/v/$iter"][:, 1, 1]
η = file["timeseries/η/$iter"][:, 1, 1]
t = file["timeseries/t/$iter"]
titlestr = @sprintf("Geostrophic adjustment at t = %.1f hours", t / hours)
v_plot = plot(xv / kilometers, v, linewidth = 2, title = titlestr,
label = "", xlabel = "x (km)", ylabel = "v (m s⁻¹)", ylims = (-U, U))
u_plot = plot(xu / kilometers, u, linewidth = 2,
label = "", xlabel = "x (km)", ylabel = "u (m s⁻¹)", ylims = (-2e-3, 2e-3))
η_plot = plot(xη / kilometers, η, linewidth = 2,
label = "", xlabel = "x (km)", ylabel = "η (m)", ylims = (-η₀/10, 2η₀))
plot(v_plot, u_plot, η_plot, layout = (3, 1), size = (800, 600))
end
close(file)This page was generated using Literate.jl.