-
Notifications
You must be signed in to change notification settings - Fork 188
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
(0.90.4) Implement three-dimensional StokesDrift 🌊 #3384
Conversation
* Update StokesDrifts.jl Adds horizontal Stokes drift gradient terms and a term due to the vertical gradient of the vertical Stokes drift component. These equations follow from the governing equation used by Oceananigans. I think I got the interpolation right but it should be checked. A test case/example is being developed * Remove unnecessary gradients * Create Spatially_varying_stokes_drift.jl * Update Spatially_varying_stokes_drift.jl Fix bug - was using old variable names
@BrodiePearson posted the following on #3392: I also added an example code that utilizes the new
|
Correction to my last message, I accidentally included a second prime. The form of the Stokes drift is actually: |
And I want to add that we can only use (I guess, users in fact do not have a choice but to use a solenoidal Stokes drift due to the way our code is written...) As for an example, it could be nice to come up with an example that illustrates the flow associated with a traveling wave packet. It should look like a dipole. |
@BrodiePearson I refactored I put together this script that reproduces the "deep Eulerian return flow" from McIntyre (1981): using Oceananigans
using Oceananigans.Units
using GLMakie
ϵ = 0.1
λ = 60 # meters
g = 9.81
const k = 2π / λ
c = sqrt(g / k)
const δ = 1kilometer
const cᵍ = c / 2
const Uˢ = ϵ^2 * c
@inline A(ξ) = exp(- ξ^2 / 2δ^2)
@inline A′(ξ) = - ξ / δ^2 * A(ξ)
@inline A′′(ξ) = (ξ^2 / δ^2 - 1) * A(ξ) / δ^2
# Write the Stokes drift as
#
# uˢ(x, z, t) = A(x, t) * ûˢ(z)
#
# which implies
@inline ûˢ(z) = Uˢ * exp(2k * z)
@inline uˢ(x, z, t) = A(x - cᵍ * t) * ûˢ(z)
@inline ∂z_uˢ(x, z, t) = 2k * A(x - cᵍ * t) * ûˢ(z)
@inline ∂t_uˢ(x, z, t) = - cᵍ * A′(x - cᵍ * t) * ûˢ(z)
# Note that if uˢ represents the solenoidal component of the Stokes drift,
# then
#
# ```math
# ∂z_wˢ = - ∂x_uˢ = - A′ * ûˢ .
# ```
#
# We therefore find that
#
# ```math
# wˢ = - A′ / 2k * ûˢ
# ```
#
# and
@inline ∂x_wˢ(x, z, t) = - 1 / 2k * A′′(x - cᵍ * t) * ûˢ(z)
@inline ∂t_wˢ(x, z, t) = + cᵍ / 2k * A′′(x - cᵍ * t) * ûˢ(z)
stokes_drift = StokesDrift(; ∂z_uˢ, ∂t_uˢ, ∂t_wˢ, ∂x_wˢ)
grid = RectilinearGrid(size = (256, 64),
x = (-5kilometers, 15kilometers),
z = (-512, 0),
topology = (Periodic, Flat, Bounded))
model = NonhydrostaticModel(; grid, stokes_drift,
tracers = :b,
buoyancy = BuoyancyTracer(),
timestepper = :RungeKutta3)
# Set Lagrangian-mean flow equal to uˢ,
uᵢ(x, z) = uˢ(x, z, 0)
# And put in a stable stratification,
N² = 0
bᵢ(x, z) = N² * z
set!(model, u=uᵢ, b=bᵢ)
Δx = xspacings(grid, Center())
Δt = 0.2 * Δx / cᵍ
simulation = Simulation(model; Δt, stop_iteration = 600)
progress(sim) = @info string("Iter: ", iteration(sim), ", time: ", prettytime(sim))
simulation.callbacks[:progress] = Callback(progress, IterationInterval(10))
filename = "surface_wave_induced_flow.jld2"
outputs = model.velocities
simulation.output_writers[:jld2] = JLD2OutputWriter(model, outputs; filename,
schedule = IterationInterval(10),
overwrite_existing = true)
run!(simulation)
ut = FieldTimeSeries(filename, "u")
wt = FieldTimeSeries(filename, "w")
times = ut.times
Nt = length(times)
n = Observable(1)
un = @lift interior(ut[$n], :, 1, :)
wn = @lift interior(wt[$n], :, 1, :)
xu, yu, zu = nodes(ut)
xw, yw, zw = nodes(wt)
fig = Figure(resolution=(800, 300))
axu = Axis(fig[1, 1], xlabel="x (m)", ylabel="z (m)")
axw = Axis(fig[1, 2], xlabel="x (m)", ylabel="z (m)")
heatmap!(axu, xu, zu, un)
heatmap!(axw, xw, zw, wn)
record(fig, "surface_wave_induced_flow.mp4", 1:Nt, framerate=12) do nn
n[] = nn
end The result is surface_wave_induced_flow.mp4where the left panel is @BrodiePearson I don't think we should add a new example for this feature (examples are expensive, because they have to run every time we run CI / build the documentation). However, another avenue to keep some code around is to add a "validation" case. If you're up for that, I'll move your code there, along with the above example. Let me know and then we can possibly merge this great addition. |
@BrodiePearson I've also added a docstring for |
Small clarifications (the order of operations in Lines 16 vs. 17 had me confused for a minute...I'm guessing different multiplication operators have different priority relative to division in Julia?)
@glwagner These changes look great! I fixed some typos in the refactored file, which were terms that would not have affected your new validation case. I moved my original example to validation and provided some minor tweaks to your first validation case. I did not look much at the |
|
||
pt = parameters_tuple(sw) | ||
X = node(i, j, k, grid, c, c, f) | ||
∂x_wˢ = sw.∂x_wˢ(X..., time, pt...) | ||
∂z_uˢ = sw.∂z_uˢ(X..., time, pt...) | ||
∂y_wˢ = sw.∂y_wˢ(X..., time, pt...) | ||
∂z_vˢ = sw.∂y_wˢ(X..., time, pt...) | ||
∂z_vˢ = sw.∂z_vˢ(X..., time, pt...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should add two validation tests, one for stokes drift in u, w and one for v, w.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just added a Langmuir validation test, forced by a Stokes drift jet in the y-direction, which is simply a rotated version of the validation test forced by an x-oriented Stokes drift jet. The resulting velocities and Reynolds stress components mirror each other as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you prefer, it would be more simple to switch your 2D validation case to be in the y-z direction, but the Langmuir cases together utilize all horizontal and vertical derivative terms in StokesDrifts.jl
.
Ok great then I think this is close. I'll just add a |
…nigans.jl into glw-bp/three-d-stokes-drift
…A/Oceananigans.jl into glw-bp/three-d-stokes-drift
…nigans.jl into glw-bp/three-d-stokes-drift
@glwagner merge at will, I won't do anything else |
I think the example for 3D Stokes drift can be improved to be more realistic. Note that it is confusing to use |
This PR implements a
StokesDrift
abstraction in which the Stokes shear and tendency terms may be functions ofx, y, z, t
rather than onlyz, t
as inUniformStokesDrift
.I'll post some results from a modified version of the
langmuir_instability.jl
example here when we have them.Could be applicable to simulations of wave-affected turbulence in sea ice leads.
With @BrodiePearson and Ara Lee.