Skip to content

Commit

Permalink
Dry rising thermal bubble verification experiment (#31)
Browse files Browse the repository at this point in the history
Dry rising thermal bubble verification experiment
  • Loading branch information
ali-ramadhan authored Jan 29, 2020
2 parents 766c9d8 + 142df76 commit acd2b95
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ notes/*.log
notes/*.gz

# Various output
*.png
*.gif
*.mp4
99 changes: 99 additions & 0 deletions examples/dry_adiabatic_atmosphere.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using JULES, Oceananigans
using Plots
using Printf

Nx = Ny = 1
Nz = 32
L = 10e3

grid = RegularCartesianGrid(size=(Nx, Ny, Nz), halo=(2, 2, 2),
x=(0, L), y=(0, L), z=(0, L))
#####
##### Dry adiabatic atmosphere
#####

gas = IdealGas()
Rᵈ, cₚ, cᵥ = gas.Rᵈ, gas.cₚ, gas.cᵥ

g = 9.80665
pₛ = 100000
θₛ = 300
πₛ = 1

θ(x, y, z) = θₛ
π(x, y, z) = πₛ - g*z / (cₚ*θₛ)
p(x, y, z) = pₛ * π(x, y, z)^(cₚ/Rᵈ)
ρ(x, y, z) = pₛ / (Rᵈ * θ(x, y, z)) * π(x, y, z)^(cᵥ/Rᵈ)
Θ(x, y, z) = ρ(x, y, z) * θ(x, y, z)

#####
##### Create model
#####

model = CompressibleModel(grid=grid, buoyancy=gas, reference_pressure=pₛ,
prognostic_temperature=ModifiedPotentialTemperature(),
tracers=(:Θᵐ,))

#####
##### Set initial conditions
#####

set!(model.density, ρ)
set!(model.tracers.Θᵐ, θ)

# Initial profiles
Θ₀_prof = model.tracers.Θᵐ[1, 1, 1:Nz]
ρ₀_prof = model.density[1, 1, 1:Nz]

# Arrays we will use to store a time series of ρw(z = L/2).
times = [model.clock.time]
ρw_ts = [model.momenta.ρw[1, 1, Int(Nz/2)]]

#####
##### Time step and keep plotting vertical profiles of ρθ′, ρw, and ρ′.
#####

# @animate for i=1:100
while model.clock.time < 500
time_step!(model; Δt=0.5, Nt=10)

@show model.clock.time
Θ_prof = model.tracers.Θᵐ[1, 1, 1:Nz] .- Θ₀_prof
W_prof = model.momenta.ρw[1, 1, 1:Nz+1]
ρ_prof = model.density[1, 1, 1:Nz] .- ρ₀_prof

Θ_plot = plot(Θ_prof, grid.zC, xlim=(-2.5, 2.5), xlabel="Theta_prime", ylabel="z (m)", label="")
W_plot = plot(W_prof, grid.zF, xlim=(-1.2, 1.2), xlabel="rho*w", label="")
ρ_plot = plot(ρ_prof, grid.zC, xlim=(-0.01, 0.01), xlabel="rho", label="")

t_str = @sprintf("t = %d s", model.clock.time)
display(plot(Θ_plot, W_plot, ρ_plot, title=["" "$t_str" ""], layout=(1, 3), show=true))

push!(times, model.clock.time)
push!(ρw_ts, model.momenta.ρw[1, 1, Int(Nz/2)])
end

#####
##### Plot the initial profile and the hydrostatically balanced profile.
#####

ρ∞_prof = model.density[1, 1, 1:Nz]

θ₀_prof = Θ₀_prof ./ ρ₀_prof
θ∞_prof = model.tracers.Θᵐ[1, 1, 1:Nz] ./ ρ∞_prof

θ_plot = plot(θ₀_prof, grid.zC, xlabel="theta (K)", ylabel="z (m)", label="initial", legend=:topleft)
plot!(θ_plot, θ∞_prof, grid.zC, label="balanced")

ρ_plot = plot(ρ₀_prof, grid.zC, xlabel="rho (kg/m³)", ylabel="z (m)", label="initial")
plot!(ρ_plot, ρ∞_prof, grid.zC, label="balanced")

display(plot(θ_plot, ρ_plot, show=true))


#####
##### Plot timeseries of maximum ρw
#####

plot(times, ρw_ts, linewidth=2, xlabel="time (s)", ylabel="rho*w(z=$(Int(L/2)) m)", label="", show=true)

40 changes: 18 additions & 22 deletions src/Operators/compressible_operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ using Oceananigans: AbstractGrid
#### Moist density
####

@inline ρᵐ(i, j, k, grid, ρᵈ, C) = @inbounds ρᵈ[i, j, k] # * (1 + ...)
@inline ρᵐ(i, j, k, grid, ρᵈ, C) = @inbounds ρᵈ[i, j, k] # TODO: * (1 + ...)
@inline ρᵈ_over_ρᵐ(i, j, k, grid, ρᵈ, C) = @inbounds ρᵈ[i, j, k] / ρᵐ(i, j, k, grid, ρᵈ, C)

@inline U_over_ρ(i, j, k, grid, U, ρᵈ) = @inbounds U[i, j, k] / ℑxᶠᵃᵃ(i, j, k, grid, ρᵈ)
@inline V_over_ρ(i, j, k, grid, V, ρᵈ) = @inbounds V[i, j, k] / ℑyᵃᶠᵃ(i, j, k, grid, ρᵈ)
@inline W_over_ρ(i, j, k, grid, W, ρᵈ) = @inbounds W[i, j, k] / ℑzᵃᵃᶠ(i, j, k, grid, ρᵈ)
@inline C_over_ρ(i, j, k, grid, C, ρᵈ) = @inbounds C[i, j, k] / ρᵈ[i, j, k]

####
#### Coriolis terms
####


@inline x_f_cross_U(i, j, k, grid::AbstractGrid{FT}, ::Nothing, Ũ) where FT = zero(FT)
@inline y_f_cross_U(i, j, k, grid::AbstractGrid{FT}, ::Nothing, Ũ) where FT = zero(FT)
@inline z_f_cross_U(i, j, k, grid::AbstractGrid{FT}, ::Nothing, Ũ) where FT = zero(FT)
Expand All @@ -27,17 +31,15 @@ using Oceananigans: AbstractGrid
@inline advective_tracer_flux_z(i, j, k, grid, W, C, ρᵈ) = Az_ψᵃᵃᵃ(i, j, k, grid, W) * ℑzᵃᵃᶠ(i, j, k, grid, C) / ℑzᵃᵃᶠ(i, j, k, grid, ρᵈ)

@inline function div_flux(i, j, k, grid, ρᵈ, U, V, W, C)
1/Vᵃᵃᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_tracer_flux_x, U, C, ρᵈ) +
δyᵃᶜᵃ(i, j, k, grid, advective_tracer_flux_y, V, C, ρᵈ) +
δzᵃᵃᶜ(i, j, k, grid, advective_tracer_flux_z, W, C, ρᵈ))
return 1/Vᵃᵃᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, advective_tracer_flux_x, U, C, ρᵈ) +
δyᵃᶜᵃ(i, j, k, grid, advective_tracer_flux_y, V, C, ρᵈ) +
δzᵃᵃᶜ(i, j, k, grid, advective_tracer_flux_z, W, C, ρᵈ))
end

####
#### Diffusion
####

@inline C_over_ρ(i, j, k, grid, C, ρ) = @inbounds C[i, j, k] / ρ[i, j, k]

@inline diffusive_flux_x(i, j, k, grid, κᶠᶜᶜ, ρᵈ, C) = κᶠᶜᶜ * Axᵃᵃᶠ(i, j, k, grid) * δxᶠᵃᵃ(i, j, k, grid, C_over_ρ, C, ρᵈ)
@inline diffusive_flux_y(i, j, k, grid, κᶜᶠᶜ, ρᵈ, C) = κᶜᶠᶜ * Ayᵃᵃᶠ(i, j, k, grid) * δyᵃᶠᵃ(i, j, k, grid, C_over_ρ, C, ρᵈ)
@inline diffusive_flux_z(i, j, k, grid, κᶜᶜᶠ, ρᵈ, C) = κᶜᶜᶠ * Azᵃᵃᵃ(i, j, k, grid) * δzᵃᵃᶠ(i, j, k, grid, C_over_ρ, C, ρᵈ)
Expand All @@ -64,33 +66,28 @@ end
@inline momentum_flux_ρwv(i, j, k, grid, ρᵈ, V, W) = ℑzᵃᵃᶠ(i, j, k, grid, Ay_ψᵃᵃᶠ, V) * ℑyᵃᶠᵃ(i, j, k, grid, W) / ℑyzᵃᶠᶠ(i, j, k, grid, ρᵈ)
@inline momentum_flux_ρww(i, j, k, grid, ρᵈ, W) = @inbounds ℑzᵃᵃᶜ(i, j, k, grid, Az_ψᵃᵃᵃ, W) * ℑzᵃᵃᶜ(i, j, k, grid, W) / ρᵈ[i, j, k]


@inline function div_ρuũ(i, j, k, grid, ρᵈ, Ũ)
return 1/Vᵃᵃᶜ(i, j, k, grid) * (δxᶠᵃᵃ(i, j, k, grid, momentum_flux_ρuu, ρᵈ, Ũ.ρu) +
δyᵃᶜᵃ(i, j, k, grid, momentum_flux_ρuv, ρᵈ, Ũ.ρu, Ũ.ρv) +
δzᵃᵃᶜ(i, j, k, grid, momentum_flux_ρuw, ρᵈ, Ũ.ρu, Ũ.ρw))
return 1/Vᵃᵃᶜ(i, j, k, grid) * ( δxᶠᵃᵃ(i, j, k, grid, momentum_flux_ρuu, ρᵈ, Ũ.ρu)
+ δyᵃᶜᵃ(i, j, k, grid, momentum_flux_ρuv, ρᵈ, Ũ.ρu, Ũ.ρv)
+ δzᵃᵃᶜ(i, j, k, grid, momentum_flux_ρuw, ρᵈ, Ũ.ρu, Ũ.ρw))
end

@inline function div_ρvũ(i, j, k, grid, ρᵈ, Ũ)
return 1/Vᵃᵃᶜ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, momentum_flux_ρvu, ρᵈ, Ũ.ρu, Ũ.ρv) +
δyᵃᶠᵃ(i, j, k, grid, momentum_flux_ρvv, ρᵈ, Ũ.ρv) +
δzᵃᵃᶜ(i, j, k, grid, momentum_flux_ρvw, ρᵈ, Ũ.ρv, Ũ.ρw))
return 1/Vᵃᵃᶜ(i, j, k, grid) * ( δxᶜᵃᵃ(i, j, k, grid, momentum_flux_ρvu, ρᵈ, Ũ.ρu, Ũ.ρv)
+ δyᵃᶠᵃ(i, j, k, grid, momentum_flux_ρvv, ρᵈ, Ũ.ρv)
+ δzᵃᵃᶜ(i, j, k, grid, momentum_flux_ρvw, ρᵈ, Ũ.ρv, Ũ.ρw))
end

@inline function div_ρwũ(i, j, k, grid, ρᵈ, Ũ)
return 1/Vᵃᵃᶠ(i, j, k, grid) * (δxᶜᵃᵃ(i, j, k, grid, momentum_flux_ρwu, ρᵈ, Ũ.ρu, Ũ.ρv) +
δyᵃᶜᵃ(i, j, k, grid, momentum_flux_ρwv, ρᵈ, Ũ.ρv, Ũ.ρw) +
δzᵃᵃᶠ(i, j, k, grid, momentum_flux_ρww, ρᵈ, Ũ.ρw))
return 1/Vᵃᵃᶠ(i, j, k, grid) * ( δxᶜᵃᵃ(i, j, k, grid, momentum_flux_ρwu, ρᵈ, Ũ.ρu, Ũ.ρw)
+ δyᵃᶜᵃ(i, j, k, grid, momentum_flux_ρwv, ρᵈ, Ũ.ρv, Ũ.ρw)
+ δzᵃᵃᶠ(i, j, k, grid, momentum_flux_ρww, ρᵈ, Ũ.ρw))
end

####
#### Viscous dissipation
####

@inline U_over_ρ(i, j, k, grid, U, ρᵈ) = @inbounds U[i, j, k] / ℑxᶠᵃᵃ(i, j, k, grid, ρᵈ)
@inline V_over_ρ(i, j, k, grid, V, ρᵈ) = @inbounds V[i, j, k] / ℑyᵃᶠᵃ(i, j, k, grid, ρᵈ)
@inline W_over_ρ(i, j, k, grid, W, ρᵈ) = @inbounds W[i, j, k] / ℑzᵃᵃᶠ(i, j, k, grid, ρᵈ)

@inline viscous_flux_ux(i, j, k, grid, μ_ccc, ρᵈ, U) = μ_ccc * ℑxᶜᵃᵃ(i, j, k, grid, Axᵃᵃᶜ) * δxᶜᵃᵃ(i, j, k, grid, U_over_ρ, U, ρᵈ)
@inline viscous_flux_uy(i, j, k, grid, μ_ffc, ρᵈ, U) = μ_ffc * ℑyᵃᶠᵃ(i, j, k, grid, Ayᵃᵃᶜ) * δyᵃᶠᵃ(i, j, k, grid, U_over_ρ, U, ρᵈ)
@inline viscous_flux_uz(i, j, k, grid, μ_fcf, ρᵈ, U) = μ_fcf * ℑzᵃᵃᶠ(i, j, k, grid, Azᵃᵃᵃ) * δzᵃᵃᶠ(i, j, k, grid, U_over_ρ, U, ρᵈ)
Expand Down Expand Up @@ -120,4 +117,3 @@ end
δyᵃᶜᵃ(i, j, k, grid, viscous_flux_wy, μ, ρᵈ, W) +
δzᵃᵃᶠ(i, j, k, grid, viscous_flux_wz, μ, ρᵈ, W))
end

2 changes: 1 addition & 1 deletion src/buoyancy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ IdealGas(FT=Float64; Rᵈ=Rᵈ_air, Rᵛ=Rᵛ_air, cₚ=cₚ_dry, cᵥ=cᵥ_dry,
#### Buoyancy term
####

@inline buoyancy_perturbation(i, j, k, grid, grav, ρᵈ, C) = grav * ρᵐ(i, j, k, grid, ρᵈ, C)
@inline buoyancy_perturbation(i, j, k, grid, g, ρᵈ, C) = g * ρᵐ(i, j, k, grid, ρᵈ, C)
1 change: 0 additions & 1 deletion src/pressure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@ const MPT = ModifiedPotentialTemperature
@inline ∂p∂x(i, j, k, grid, pt::Entropy, gas::IG, ρ, C) = ∂xᶠᵃᵃ(i, j, k, grid, p, pt, gas, ρ, C)
@inline ∂p∂y(i, j, k, grid, pt::Entropy, gas::IG, ρ, C) = ∂yᵃᶠᵃ(i, j, k, grid, p, pt, gas, ρ, C)
@inline ∂p∂z(i, j, k, grid, pt::Entropy, gas::IG, ρ, C) = ∂zᵃᵃᶠ(i, j, k, grid, p, pt, gas, ρ, C)

4 changes: 2 additions & 2 deletions src/right_hand_sides.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
####

const grav = 9.80665
const μ = 1e-2
const κ = 1e-2
const μ = 0.5
const κ = 0.5

####
#### Element-wise forcing and right-hand-side calculations
Expand Down
4 changes: 1 addition & 3 deletions src/time_stepping.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function time_step!(model::CompressibleModel; Δt, Nt=1)
# On third RK3 step, we update Φ⁺ instead of model.intermediate_vars
Φ⁺ = merge(Ũ, C̃, (ρ=ρᵈ,))

# On the first and second RK3 steps we can to update intermediate Ũ and C̃.
# On the first and second RK3 steps we want to update intermediate Ũ and C̃.
Ũ_names = propertynames(Ũ)
IV_Ũ_vals = [getproperty(IV, U) for U in Ũ_names]
IV_Ũ = NamedTuple{Ũ_names}(IV_Ũ_vals)
Expand Down Expand Up @@ -88,8 +88,6 @@ function time_step!(model::CompressibleModel; Δt, Nt=1)

compute_right_hand_sides!(compute_rhs_args...)

fill_halo_regions!(R.ρw.data, hpbcs_np, arch, grid)

# n, Δτ = acoustic_time_steps(rk3_iter)
# acoustic_time_stepping!(Ũ, ρ, C, F, R; n=n, Δτ=Δτ)

Expand Down
138 changes: 138 additions & 0 deletions verification/dry_rising_thermal_bubble/dry_rising_thermal_bubble.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
"""
This example sets up a dry, warm thermal bubble perturbation in a uniform
lateral mean flow which buoyantly rises.
"""

using Printf
using Plots
using Oceananigans
using JULES
using JULES: Π

const km = 1000
const hPa = 100

Lx = 20km
Lz = 10km

Δ = 125 # grid spacing [m]

Nx = Int(Lx/Δ)
Ny = 1
Nz = Int(Lz/Δ)

grid = RegularCartesianGrid(size=(Nx, Ny, Nz), halo=(2, 2, 2),
x=(-Lx/2, Lx/2), y=(-Lx/2, Lx/2), z=(0, Lz))

#####
##### Dry thermal bubble perturbation
#####

xᶜ, zᶜ = 0km, 2km
xʳ, zʳ = 2km, 2km

@inline L(x, y, z) = (((x - xᶜ) / xʳ)^2 + ((z - zᶜ) / zʳ)^2)
@inline function θ′(x, y, z)
= L(x, y, z)
return (ℓ <= 1) * 2cos/2 * ℓ)^2
end

#####
##### Set up model
#####

gas = IdealGas()
Rᵈ, cₚ, cᵥ = gas.Rᵈ, gas.cₚ, gas.cᵥ

g = 9.80665
pₛ = 1000hPa
θₛ = 300
πₛ = 1

θ₀(x, y, z) = θₛ
π₀(x, y, z) = πₛ - g*z / (cₚ*θₛ)
p₀(x, y, z) = pₛ * π₀(x, y, z)^(cₚ/Rᵈ)
ρ₀(x, y, z) = pₛ / (Rᵈ * θ₀(x, y, z)) * π₀(x, y, z)^(cᵥ/Rᵈ)
Θ₀(x, y, z) = ρ₀(x, y, z) * θ₀(x, y, z)

const τ⁻¹ = 1 # Damping/relaxation time scale [s⁻¹]. This is very strong damping.
const Δμ = 0.1Lz # Sponge layer width [m] set to 10% of the domain height.
@inline μ(z, Lz) = τ⁻¹ * exp(-(Lz-z) / Δμ)

@inline Fw(i, j, k, grid, t, Ũ, C̃, p) = @inbounds (t <= 500) * -μ(grid.zF[k], grid.Lz) *.ρw[i, j, k]
forcing = ModelForcing(w=Fw)

model = CompressibleModel(grid=grid, buoyancy=gas, reference_pressure=pₛ,
prognostic_temperature=ModifiedPotentialTemperature(),
tracers=(:Θᵐ,), forcing=forcing)

set!(model.density, ρ₀)
set!(model.tracers.Θᵐ, Θ₀)

#####
##### Run a dry adiabatic atmosphere to hydrostatic balance
#####

while model.clock.time < 500
@printf("t = %.2f s\n", model.clock.time)
time_step!(model, Δt=0.2, Nt=100)
end

#####
##### Now add the cold bubble perturbation.
#####

ρʰᵈ = model.density.data[1:Nx, 1, 1:Nz]
Θʰᵈ = model.tracers.Θᵐ.data[1:Nx, 1, 1:Nz]

xC, zC = grid.xC, grid.zC
ρ, Θ = model.density, model.tracers.Θᵐ
for k in 1:Nz, i in 1:Nx
θ = Θ[i, 1, k] / ρ[i, 1, k] + θ′(xC[i], 0, zC[k])
π = Π(i, 1, k, grid, gas, Θ)

ρ[i, 1, k] = pₛ / (Rᵈ*θ) * π^(cᵥ/Rᵈ)
Θ[i, 1, k] = ρ[i, 1, k] * θ
end

ρ_plot = contour(model.grid.xC ./ km, model.grid.zC ./ km, rotr90.data[1:Nx, 1, 1:Nz] .- ρʰᵈ),
fill=true, levels=10, xlims=(-5, 5), clims=(-0.008, 0.008), color=:balance, dpi=200)
savefig(ρ_plot, "rho_prime_initial_condition.png")

θ_slice = rotr90.data[1:Nx, 1, 1:Nz] ./ ρ.data[1:Nx, 1, 1:Nz])
Θ_plot = contour(model.grid.xC ./ km, model.grid.zC ./ km, θ_slice,
fill=true, levels=10, xlims=(-5, 5), color=:thermal, dpi=200)
savefig(Θ_plot, "theta_initial_condition.png")

#####
##### Watch the thermal bubble rise!
#####

for n in 1:200
time_step!(model, Δt=0.1, Nt=50)

@printf("t = %.2f s\n", model.clock.time)
xC, yC, zC = model.grid.xC ./ km, model.grid.yC ./ km, model.grid.zC ./ km
xF, yF, zF = model.grid.xF ./ km, model.grid.yF ./ km, model.grid.zF ./ km

j = 1
u_slice = rotr90(model.momenta.ρu.data[1:Nx, j, 1:Nz] ./ model.density.data[1:Nx, j, 1:Nz])
w_slice = rotr90(model.momenta.ρw.data[1:Nx, j, 1:Nz] ./ model.density.data[1:Nx, j, 1:Nz])
ρ_slice = rotr90(model.density.data[1:Nx, j, 1:Nz] .- ρʰᵈ)
θ_slice = rotr90(model.tracers.Θᵐ.data[1:Nx, j, 1:Nz] ./ model.density.data[1:Nx, j, 1:Nz])

u_title = @sprintf("u, t = %d s", round(Int, model.clock.time))
pu = contour(xC, zC, u_slice, title=u_title, fill=true, levels=10, xlims=(-5, 5), color=:balance, clims=(-10, 10))
pw = contour(xC, zC, w_slice, title="w", fill=true, levels=10, xlims=(-5, 5), color=:balance, clims=(-10, 10))
= contour(xC, zC, ρ_slice, title="rho_prime", fill=true, levels=10, xlims=(-5, 5), color=:balance, clims=(-0.006, 0.006))
= contour(xC, zC, θ_slice, title="theta", fill=true, levels=10, xlims=(-5, 5), color=:thermal, clims=(299.9, 302))

p = plot(pu, pw, pρ, pθ, layout=(2, 2), dpi=200, show=true)
savefig(p, @sprintf("thermal_bubble_%03d.png", n))
end

θ_1000 = (model.tracers.Θᵐ.data[1:Nx, 1, 1:Nz] ./ model.density.data[1:Nx, 1, 1:Nz]) .- θₛ
w_1000 = (model.momenta.ρw.data[1:Nx, 1, 1:Nz] ./ model.density.data[1:Nx, 1, 1:Nz])

@printf("θ′: min=%.2f, max=%.2f\n", minimum(θ_1000), maximum(θ_1000))
@printf("w: min=%.2f, max=%.2f\n", minimum(w_1000), maximum(w_1000))

0 comments on commit acd2b95

Please sign in to comment.