Skip to content

Commit

Permalink
Add Lucas' thesis
Browse files Browse the repository at this point in the history
  • Loading branch information
Arpit-Babbar committed Jul 16, 2024
1 parent 1659683 commit c405e97
Show file tree
Hide file tree
Showing 10 changed files with 2,054 additions and 0 deletions.
275 changes: 275 additions & 0 deletions examples/elixir_moist_euler_EC_bubble.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@

using OrdinaryDiffEq
using Trixi, TrixiAtmo
using TrixiAtmo: cons2aeqpot, source_terms_geopotential
using NLsolve: nlsolve

###############################################################################
# semidiscretization of the compressible moist Euler equations

equations = CompressibleMoistEulerEquations2D()

function moist_state(y, dz, y0, r_t0, theta_e0, equations::CompressibleMoistEulerEquations2D)
@unpack p_0, g, c_pd, c_pv, c_vd, c_vv, R_d, R_v, c_pl, L_00 = equations
(p, rho, T, r_t, r_v, rho_qv, theta_e) = y
p0 = y0[1]

F = zeros(7,1)
rho_d = rho / (1 + r_t)
p_d = R_d * rho_d * T
T_C = T - 273.15
p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C))
L = L_00 - (c_pl - c_pv) * T

F[1] = (p - p0) / dz + g * rho
F[2] = p - (R_d * rho_d + R_v * rho_qv) * T
# H = 1 is assumed
F[3] = (theta_e - T * (p_d / p_0)^(-R_d / (c_pd + c_pl * r_t)) *
exp(L * r_v / ((c_pd + c_pl * r_t) * T)))
F[4] = r_t - r_t0
F[5] = rho_qv - rho_d * r_v
F[6] = theta_e - theta_e0
a = p_vs / (R_v * T) - rho_qv
b = rho - rho_qv - rho_d
# H=1 => phi=0
F[7] = a+b-sqrt(a*a+b*b)

return F
end

function generate_function_of_y(dz, y0, r_t0, theta_e0, equations::CompressibleMoistEulerEquations2D)
function function_of_y(y)
return moist_state(y, dz, y0, r_t0, theta_e0, equations)
end
end
#Create Initial atmosphere by generating a layer data set
struct AtmossphereLayers{RealT<:Real}
equations::CompressibleMoistEulerEquations2D
# structure: 1--> i-layer (z = total_hight/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql

Check warning on line 48 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
LayerData::Matrix{RealT} # Contains the layer data for each height
total_hight::RealT # Total height of the atmosphere

Check warning on line 50 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
preciseness::Int # Space between each layer data (dz)
layers::Int # Amount of layers (total height / dz)
ground_state::NTuple{2, RealT} # (rho_0, p_tilde_0) to define the initial values at height z=0
equivalentpotential_temperature::RealT # Value for theta_e since we have a constant temperature theta_e0=theta_e.
mixing_ratios::NTuple{2, RealT} # Constant mixing ratio. Also defines initial guess for rho_qv0 = r_v0 * rho_0.
end


function AtmossphereLayers(equations ; total_hight=10010.0, preciseness=10, ground_state=(1.4, 100000.0), equivalentpotential_temperature=320, mixing_ratios=(0.02, 0.02), RealT=Float64)

Check warning on line 59 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
@unpack kappa, p_0, c_pd, c_vd, c_pv, c_vv, R_d, R_v, c_pl = equations
rho0, p0 = ground_state
r_t0, r_v0 = mixing_ratios
theta_e0 = equivalentpotential_temperature

rho_qv0 = rho0 * r_v0
T0 = theta_e0
y0 = [p0, rho0, T0, r_t0, r_v0, rho_qv0, theta_e0]

n = convert(Int, total_hight / preciseness)

Check warning on line 69 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
dz = 0.01
LayerData = zeros(RealT, n+1, 4)

F = generate_function_of_y(dz, y0, r_t0, theta_e0, equations)
sol = nlsolve(F, y0)
p, rho, T, r_t, r_v, rho_qv, theta_e = sol.zero

rho_d = rho / (1 + r_t)
rho_ql = rho - rho_d - rho_qv
kappa_M=(R_d * rho_d + R_v * rho_qv) / (c_pd * rho_d + c_pv * rho_qv + c_pl * rho_ql)
rho_theta = rho * (p0 / p)^kappa_M * T * (1 + (R_v / R_d) *r_v) / (1 + r_t)

LayerData[1, :] = [rho, rho_theta, rho_qv, rho_ql]
for i in (1:n)
y0 = deepcopy(sol.zero)
dz = preciseness
F = generate_function_of_y(dz, y0, r_t0, theta_e0, equations)
sol = nlsolve(F, y0)
p, rho, T, r_t, r_v, rho_qv, theta_e = sol.zero

rho_d = rho / (1 + r_t)
rho_ql = rho - rho_d - rho_qv
kappa_M=(R_d * rho_d + R_v * rho_qv) / (c_pd * rho_d + c_pv * rho_qv + c_pl * rho_ql)
rho_theta = rho * (p0 / p)^kappa_M * T * (1 + (R_v / R_d) *r_v) / (1 + r_t)

LayerData[i+1, :] = [rho, rho_theta, rho_qv, rho_ql]
end

return AtmossphereLayers{RealT}(equations, LayerData, total_hight, dz, n, ground_state, theta_e0, mixing_ratios)

Check warning on line 98 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
end

# Generate background state from the Layer data set by linearely interpolating the layers
function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, AtmosphereLayers::AtmossphereLayers)
@unpack LayerData, preciseness, total_hight = AtmosphereLayers

Check warning on line 103 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
dz = preciseness
z = x[2]
if (z > total_hight && !(isapprox(z, total_hight)))

Check warning on line 106 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".

Check warning on line 106 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
error("The atmossphere does not match the simulation domain")
end
n = convert(Int, floor(z/dz)) + 1
z_l = (n-1) * dz
(rho_l, rho_theta_l, rho_qv_l, rho_ql_l) = LayerData[n, :]
z_r = n * dz
if (z_l == total_hight)

Check warning on line 113 in examples/elixir_moist_euler_EC_bubble.jl

View workflow job for this annotation

GitHub Actions / Spell Check with Typos

"hight" should be "height" or "high".
z_r = z_l + dz
n = n-1
end
(rho_r, rho_theta_r, rho_qv_r, rho_ql_r) = LayerData[n+1, :]
rho = (rho_r * (z - z_l) + rho_l * (z_r - z)) / dz
rho_theta = rho * (rho_theta_r / rho_r * (z - z_l) + rho_theta_l / rho_l * (z_r - z)) / dz
rho_qv = rho * (rho_qv_r / rho_r * (z - z_l) + rho_qv_l / rho_l * (z_r - z)) / dz
rho_ql = rho * (rho_ql_r / rho_r * (z - z_l) + rho_ql_l / rho_l * (z_r - z)) / dz

rho, rho_e, rho_qv, rho_ql = PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::CompressibleMoistEulerEquations2D)

v1 = 60.0
v2 = 60.0
rho_v1 = rho * v1
rho_v2 = rho * v2
rho_E = rho_e + 1/2 * rho *(v1^2 + v2^2)


return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql)
end

# Add perturbation to the profile
function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::CompressibleMoistEulerEquations2D)
@unpack kappa, p_0, c_pd, c_vd, c_pv, c_vv, R_d, R_v, c_pl, L_00 = equations
xc = 2000
zc = 2000
rc = 2000
# Peak perturbation at center
Δθ = 10

r = sqrt((x[1] - xc)^2 + (x[2] - zc)^2)
rho_d = rho - rho_qv - rho_ql
kappa_M = (R_d * rho_d + R_v * rho_qv) / (c_pd * rho_d + c_pv * rho_qv + c_pl * rho_ql)
p_loc = p_0 *(R_d * rho_theta / p_0)^(1/(1-kappa_M))
T_loc = p_loc / (R_d * rho_d + R_v * rho_qv)
rho_e = (c_vd * rho_d + c_vv * rho_qv + c_pl * rho_ql) * T_loc + L_00 * rho_qv


# Assume pressure stays constant
if (r < rc && Δθ > 0)
θ_dens = rho_theta / rho * (p_loc / p_0)^(kappa_M - kappa)
θ_dens_new = θ_dens * (1 + Δθ * cospi(0.5*r/rc)^2 / 300)
rt =(rho_qv + rho_ql) / rho_d
rv = rho_qv / rho_d
θ_loc = θ_dens_new * (1 + rt)/(1 + (R_v / R_d) * rv)
if rt > 0
while true
T_loc = θ_loc * (p_loc / p_0)^kappa
T_C = T_loc - 273.15
# SaturVapor
pvs = 611.2 * exp(17.62 * T_C / (243.12 + T_C))
rho_d_new = (p_loc - pvs) / (R_d * T_loc)
rvs = pvs / (R_v * rho_d_new * T_loc)
θ_new = θ_dens_new * (1 + rt) / (1 + (R_v / R_d) * rvs)
if abs(θ_new-θ_loc) <= θ_loc * 1.0e-12
break
else
θ_loc=θ_new
end
end
else
rvs = 0
T_loc = θ_loc * (p_loc / p_0)^kappa
rho_d_new = p_loc / (R_d * T_loc)
θ_new = θ_dens_new * (1 + rt) / (1 + (R_v / R_d) * rvs)
end
rho_qv = rvs * rho_d_new
rho_ql = (rt - rvs) * rho_d_new
rho = rho_d_new * (1 + rt)
rho_d = rho - rho_qv - rho_ql
kappa_M = (R_d * rho_d + R_v * rho_qv) / (c_pd * rho_d + c_pv * rho_qv + c_pl * rho_ql)
rho_theta = rho * θ_dens_new * (p_loc / p_0)^(kappa - kappa_M)
rho_e = (c_vd * rho_d + c_vv * rho_qv + c_pl * rho_ql) * T_loc + L_00 * rho_qv
end

return SVector(rho, rho_e, rho_qv, rho_ql)
end

AtmossphereData = AtmossphereLayers(equations)

function initial_condition_moist(x, t, equations)
return initial_condition_moist_bubble(x, t, equations, AtmossphereData)
end

initial_condition = initial_condition_moist

boundary_condition = (x_neg=boundary_condition_periodic,
x_pos=boundary_condition_periodic,
y_neg=boundary_condition_periodic,
y_pos=boundary_condition_periodic)

source_term = source_terms_geopotential

###############################################################################
# Get the DG approximation space

polydeg = 4
basis = LobattoLegendreBasis(polydeg)


surface_flux = flux_chandrashekar
volume_flux = flux_chandrashekar


volume_integral=VolumeIntegralFluxDifferencing(volume_flux)

solver = DGSEM(basis, surface_flux, volume_integral)


coordinates_min = (0.0, 0.0)
coordinates_max = (4000.0, 4000.0)

cells_per_dimension = (16, 16)

# Create curved mesh with 16 x 16 elements
mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max)

###############################################################################
# create the semi discretization object

semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions=boundary_condition,
source_terms=source_term)

###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 30.0)

ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 1000
solution_variables = cons2aeqpot

analysis_callback = AnalysisCallback(semi, interval=analysis_interval, extra_analysis_errors=(:entropy_conservation_error, ))

alive_callback = AliveCallback(analysis_interval=analysis_interval)

save_solution = SaveSolutionCallback(interval=1000,
save_initial_solution=true,
save_final_solution=true,
solution_variables=solution_variables)

stepsize_callback = StepsizeCallback(cfl=0.8)

callbacks = CallbackSet(summary_callback,
analysis_callback,
alive_callback,
save_solution,
stepsize_callback)

###############################################################################
# run the simulation


sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep=false, callback=callbacks);

summary_callback() # print the timer summary
Loading

0 comments on commit c405e97

Please sign in to comment.