From c405e978569791e9e94699a69f22aa7a58e97756 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Sun, 7 Jul 2024 10:11:55 +0530 Subject: [PATCH 01/17] Add Lucas' thesis --- examples/elixir_moist_euler_EC_bubble.jl | 275 +++++ examples/elixir_moist_euler_dry_bubble.jl | 127 ++ examples/elixir_moist_euler_moist_bubble.jl | 293 +++++ ...oist_euler_nonhydrostatic_gravity_waves.jl | 136 +++ .../elixir_moist_euler_source_terms_dry.jl | 60 + .../elixir_moist_euler_source_terms_moist.jl | 65 ++ ...ir_moist_euler_source_terms_split_moist.jl | 71 ++ src/TrixiAtmo.jl | 2 + src/equations/compressible_moist_euler_2d.jl | 1021 +++++++++++++++++ src/equations/equations.jl | 4 + 10 files changed, 2054 insertions(+) create mode 100644 examples/elixir_moist_euler_EC_bubble.jl create mode 100644 examples/elixir_moist_euler_dry_bubble.jl create mode 100644 examples/elixir_moist_euler_moist_bubble.jl create mode 100644 examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl create mode 100644 examples/elixir_moist_euler_source_terms_dry.jl create mode 100644 examples/elixir_moist_euler_source_terms_moist.jl create mode 100644 examples/elixir_moist_euler_source_terms_split_moist.jl create mode 100644 src/equations/compressible_moist_euler_2d.jl diff --git a/examples/elixir_moist_euler_EC_bubble.jl b/examples/elixir_moist_euler_EC_bubble.jl new file mode 100644 index 0000000..b8247d8 --- /dev/null +++ b/examples/elixir_moist_euler_EC_bubble.jl @@ -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 + LayerData::Matrix{RealT} # Contains the layer data for each height + total_hight::RealT # Total height of the atmosphere + 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) + @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) + 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) +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 + dz = preciseness + z = x[2] + if (z > total_hight && !(isapprox(z, total_hight))) + 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) + 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 diff --git a/examples/elixir_moist_euler_dry_bubble.jl b/examples/elixir_moist_euler_dry_bubble.jl new file mode 100644 index 0000000..b456b63 --- /dev/null +++ b/examples/elixir_moist_euler_dry_bubble.jl @@ -0,0 +1,127 @@ +using OrdinaryDiffEq +using Trixi +using TrixiAtmo +using TrixiAtmo: flux_LMARS, source_terms_geopotential, cons2drypot + +############################################################################### +# semidiscretization of the compressible moist Euler equations + +equations = CompressibleMoistEulerEquations2D() + +# Warm bubble test from paper: +# Wicker, L. J., and W. C. Skamarock, 1998: A time-splitting scheme +# for the elastic equations incorporating second-order Runge–Kutta +# time differencing. Mon. Wea. Rev., 126, 1992–1999. +function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEquations2D) + @unpack p_0, kappa, g, c_pd, c_vd, R_d, R_v = equations + xc = 10000.0 + zc = 2000.0 + r = sqrt((x[1] - xc)^2 + (x[2] - zc)^2) + rc = 2000.0 + θ_ref = 300.0 + Δθ = 0.0 + + if r <= rc + Δθ = 2 * cospi(0.5*r/rc)^2 + end + + #Perturbed state: + θ = θ_ref + Δθ # potential temperature + # π_exner = 1 - g / (c_pd * θ) * x[2] # exner pressure + # rho = p_0 / (R_d * θ) * (π_exner)^(c_vd / R_d) # density + + # calculate background pressure with assumption hydrostatic and neutral + p = p_0 * (1-kappa * g * x[2] / (R_d * θ_ref))^(c_pd / R_d) + + #calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π + rho = p / ((p / p_0)^kappa*R_d*θ) + T = p / (R_d * rho) + + v1 = 20.0 + #v1 = 0.0 + v2 = 0.0 + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_E = rho * c_vd * T + 1/2 * rho * (v1^2 + v2^2) + return SVector(rho, rho_v1, rho_v2, rho_E, zero(eltype(g)) ,zero(eltype(g))) +end + +initial_condition = initial_condition_warm_bubble + +boundary_condition = (x_neg=boundary_condition_periodic, + x_pos=boundary_condition_periodic, + y_neg=boundary_condition_slip_wall, + y_pos=boundary_condition_slip_wall) + +# Gravity source since Q_ph=0 +source_term = source_terms_geopotential + +############################################################################### +# Get the DG approximation space +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) + +surface_flux = flux_LMARS +volume_flux = flux_chandrashekar + +volume_integral=VolumeIntegralFluxDifferencing(volume_flux) + + +# Create DG solver with polynomial degree = 4 and LMARS flux as surface flux +# and the EC flux (chandrashekar) as volume flux +solver = DGSEM(basis, surface_flux, volume_integral) + +coordinates_min = (0.0, 0.0) +coordinates_max = (20000.0, 10000.0) + + +cells_per_dimension = (64, 32) + +# Create curved mesh with 64 x 32 elements +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, periodicity = (true, false)) + +# A semidiscretization collects data structures and functions for the spatial discretization +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + boundary_conditions=boundary_condition, + source_terms=source_term) + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 1000.0) + + +# Create ODE problem with time span from 0.0 to 1000.0 +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 1000 +solution_variables = cons2drypot + +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.2) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + save_solution, + stepsize_callback) + + +############################################################################### +# run the simulation +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + maxiters=1.0e7, + 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 diff --git a/examples/elixir_moist_euler_moist_bubble.jl b/examples/elixir_moist_euler_moist_bubble.jl new file mode 100644 index 0000000..0425281 --- /dev/null +++ b/examples/elixir_moist_euler_moist_bubble.jl @@ -0,0 +1,293 @@ +using OrdinaryDiffEq +using Trixi, TrixiAtmo +using TrixiAtmo: cons2aeqpot, saturation_pressure, source_terms_moist_bubble, + flux_LMARS +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 + +struct AtmossphereLayers{RealT<:Real} + equations::CompressibleMoistEulerEquations2D + # structure: 1--> i-layer (z = total_hight/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql + LayerData::Matrix{RealT} + total_hight::RealT + preciseness::Int + layers::Int + ground_state::NTuple{2, RealT} + equivalentpotential_temperature::RealT + mixing_ratios::NTuple{2, RealT} +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) + @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) + 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) +end + +# Moist bubble test case from paper: +# G.H. Bryan, J.M. Fritsch, A Benchmark Simulation for Moist Nonhydrostatic Numerical +# Models, MonthlyWeather Review Vol.130, 2917–2928, 2002, +# https://journals.ametsoc.org/view/journals/mwre/130/12/1520-0493_2002_130_2917_absfmn_2.0.co_2.xml. +function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, AtmosphereLayers::AtmossphereLayers) + @unpack LayerData, preciseness, total_hight = AtmosphereLayers + dz = preciseness + z = x[2] + if (z > total_hight && !(isapprox(z, total_hight))) + error("The atmossphere does not match the simulation domain") + end + n = convert(Int, floor((z+eps())/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) + 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 = 0.0 + v2 = 0.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 + +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 = 10000.0 + zc = 2000.0 + rc = 2000.0 + Δθ = 2.0 + + 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 + + p_v = rho_qv * R_v * T_loc + p_d = p_loc - p_v + T_C = T_loc - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + + # Aequivalentpotential temperature + a=T_loc * (p_0 / p_d)^(R_d / (c_pd + r_t * c_pl)) + b=H^(- r_v * R_v /c_pd) + L_v = L_00 + (c_pv - c_pl) * T_loc + c=exp(L_v * r_v / ((c_pd + r_t * c_pl) * T_loc)) + aeq_pot = (a * b *c) + + # Assume pressure stays constant + if (r < rc && Δθ > 0) + # Calculate background density potential temperature + θ_dens = rho_theta / rho * (p_loc / p_0)^(kappa_M - kappa) + # Calculate perturbed density potential temperature + θ_dens_new = θ_dens * (1 + Δθ * cospi(0.5*r/rc)^2 / 300) + rt =(rho_qv + rho_ql) / rho_d + rv = rho_qv / rho_d + # Calculate moist potential temperature + θ_loc = θ_dens_new * (1 + rt)/(1 + (R_v / R_d) * rv) + # Adjust varuables until the temperature is met + 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 + +# Create background atmosphere data set +AtmossphereData = AtmossphereLayers(equations) + +# Create the initial condition with the initial data set +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_slip_wall, + x_pos=boundary_condition_slip_wall, + y_neg=boundary_condition_slip_wall, + y_pos=boundary_condition_slip_wall) + +source_term = source_terms_moist_bubble + +############################################################################### +# Get the DG approximation space + +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) + +surface_flux = flux_LMARS +volume_flux = flux_chandrashekar + + +volume_integral=VolumeIntegralFluxDifferencing(volume_flux) + +solver = DGSEM(basis, surface_flux, volume_integral) + + +coordinates_min = (0.0, 0.0) +coordinates_max = (20000.0, 10000.0) + +cells_per_dimension = (64, 32) + +# Create curved mesh with 64 x 32 elements +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, + periodicity = (false, false)) + +############################################################################### +# 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, 1000.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, ), extra_analysis_integrals=(entropy, energy_total, saturation_pressure)) + +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.2) + +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 diff --git a/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl new file mode 100644 index 0000000..8d0fc12 --- /dev/null +++ b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl @@ -0,0 +1,136 @@ +using OrdinaryDiffEq +using Trixi, TrixiAtmo +using TrixiAtmo: CompressibleMoistEulerEquations2D, source_terms_geopotential, + source_terms_phase_change, + source_terms_nonhydrostatic_raylight_sponge, + cons2drypot, flux_LMARS + +############################################################################### +# semidiscretization of the compressible moist Euler equation + +# Mountain Triggered Gravity Wave test from: +# W. A. Gallus JR., J. B. Klemp, Behavior of Flow over Step Orography, Monthly Weather +# Review Vol. 128.4, pages 1153–1164, 2000, +# https://doi.org/10.1175/1520-0493(2000)128<1153:BOFOSO>2.0.CO;2. +function initial_condition_nonhydrostatic_gravity_wave(x, t, equations::CompressibleMoistEulerEquations2D) + @unpack p_0, kappa, gamma, g, c_pd, c_vd, R_d, R_v = equations + z = x[2] + T_0 = 280.0 + theta_0 = T_0 + N = 0.01 + + theta = theta_0 * exp(N^2 *z / g) + p = p_0*(1 + g^2 * inv(c_pd * theta_0 * N^2) * (exp(-z * N^2 / g) - 1))^(1/kappa) + rho = p / ((p / p_0)^kappa*R_d*theta) + T = p / (R_d * rho) + + v1 = 10 + v2 = 0 + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_E = rho * c_vd * T + 0.5 * rho * (v1^2 + v2^2) + rho_qv = 0 + rho_ql = 0 + return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) +end + +equations = CompressibleMoistEulerEquations2D() + +initial_condition = initial_condition_nonhydrostatic_gravity_wave + +function source(u, x, t, equations::CompressibleMoistEulerEquations2D) + return (source_terms_geopotential(u, equations) + + source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) + + source_terms_nonhydrostatic_raylight_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D)) +end + +source_term=source + +boundary_conditions = ( + x_neg=boundary_condition_periodic, + x_pos=boundary_condition_periodic, + y_neg=boundary_condition_slip_wall, + y_pos=boundary_condition_slip_wall, + ) + +polydeg = 4 +basis = LobattoLegendreBasis(polydeg) +surface_flux = flux_LMARS +volume_flux = flux_chandrashekar + + +volume_integral=VolumeIntegralFluxDifferencing(volume_flux) + +solver = DGSEM(basis, surface_flux, volume_integral) + + +# Deformed rectangle that has "witch of agnesi" as bottom + +function bottom(x) + h = 400.0 + a = 1000.0 + x_length = 40000.0 + # linear function cx for x in [-1,1] + c = x_length / 2 + # return (cx , f(cx)-f(c)) + return SVector(c * x , (h * a^2 * inv((c * x)^2+a^2)) - (h * a^2 * inv((c)^2+a^2))) +end + +f1(s) = SVector(-20000.0, 8000.0 * s + 8000.0) +f2(s) = SVector( 20000.0, 8000.0 * s + 8000.0) +f3(s) = SVector( 20000.0 * s , (400.0 * 1000.0^2 * inv((20000.0 * s)^2+1000.0^2))-(400.0 * 1000.0^2 * inv((20000.0)^2+1000.0^2))) +f4(s) = SVector( 20000.0 * s, 16000.0) + + +faces = (f1, f2, f3, f4) + + +# dx = 0.2*a dz = 10-200 m für (40,16) km +cells_per_dimension = (100, 80) + + +mesh = StructuredMesh(cells_per_dimension, faces, periodicity = (true, false)) + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_term, boundary_conditions=boundary_conditions) + +############################################################################### +# ODE solvers, callbacks etc. + +# t = 21.6*a/v_1 +tspan = (0.0, 2160.0) + +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 10000 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +save_solution = SaveSolutionCallback(interval=5000, + save_initial_solution=true, + save_final_solution=true, + solution_variables=cons2drypot) + + +stepsize_callback = StepsizeCallback(cfl=0.2) + +callbacks = CallbackSet(summary_callback, + analysis_callback, + alive_callback, + save_solution, + stepsize_callback); + + +############################################################################### +# run the simulation + + +sol = solve(ode, SSPRK33(), + maxiters=1.0e7, + dt=1, # 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 diff --git a/examples/elixir_moist_euler_source_terms_dry.jl b/examples/elixir_moist_euler_source_terms_dry.jl new file mode 100644 index 0000000..9d88ca8 --- /dev/null +++ b/examples/elixir_moist_euler_source_terms_dry.jl @@ -0,0 +1,60 @@ +# The same setup as tree_2d_dgsem/elixir_euler_source_terms.jl +# to verify the StructuredMesh implementation against TreeMesh + +using OrdinaryDiffEq +using Trixi, TrixiAtmo +using TrixiAtmo: source_terms_convergence_test_dry, initial_condition_convergence_test_dry + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleMoistEulerEquations2D( ;g=0.0) + +initial_condition = initial_condition_convergence_test_dry + +solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) + +coordinates_min = (0.0, 0.0) +coordinates_max = (2.0, 2.0) + +cells_per_dimension = (16, 16) + +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) + + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test_dry) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +save_solution = SaveSolutionCallback(interval=100, + save_initial_solution=true, + save_final_solution=true, + solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=1.0) + +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 diff --git a/examples/elixir_moist_euler_source_terms_moist.jl b/examples/elixir_moist_euler_source_terms_moist.jl new file mode 100644 index 0000000..08d9e39 --- /dev/null +++ b/examples/elixir_moist_euler_source_terms_moist.jl @@ -0,0 +1,65 @@ +# The same setup as tree_2d_dgsem/elixir_euler_source_terms.jl +# to verify the StructuredMesh implementation against TreeMesh + +using OrdinaryDiffEq +using Trixi, TrixiAtmo +using TrixiAtmo: source_terms_convergence_test_moist, initial_condition_convergence_test_moist + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleMoistEulerEquations2D() + +initial_condition = initial_condition_convergence_test_moist + +polydeg = 3 +basis = LobattoLegendreBasis(polydeg) + +surface_flux = flux_lax_friedrichs + +solver = DGSEM(basis, surface_flux) + +coordinates_min = (0.0, 0.0) +coordinates_max = (2.0, 2.0) + +cells_per_dimension = (4, 4) + +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) + + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test_moist) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +save_solution = SaveSolutionCallback(interval=100, + save_initial_solution=true, + save_final_solution=true, + solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=1.0) + +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 diff --git a/examples/elixir_moist_euler_source_terms_split_moist.jl b/examples/elixir_moist_euler_source_terms_split_moist.jl new file mode 100644 index 0000000..73bb0ae --- /dev/null +++ b/examples/elixir_moist_euler_source_terms_split_moist.jl @@ -0,0 +1,71 @@ +# The same setup as tree_2d_dgsem/elixir_euler_source_terms.jl +# to verify the StructuredMesh implementation against TreeMesh + +using OrdinaryDiffEq +using Trixi, TrixiAtmo +using TrixiAtmo: source_terms_convergence_test_moist, initial_condition_convergence_test_moist + +############################################################################### +# semidiscretization of the compressible Euler equations + +equations = CompressibleMoistEulerEquations2D() + +initial_condition = initial_condition_convergence_test_moist + +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 = (2.0, 2.0) + +cells_per_dimension = (4, 4) + +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) + + +semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, + source_terms=source_terms_convergence_test_moist) + + +############################################################################### +# ODE solvers, callbacks etc. + +tspan = (0.0, 2.0) +ode = semidiscretize(semi, tspan) + +summary_callback = SummaryCallback() + +analysis_interval = 100 +analysis_callback = AnalysisCallback(semi, interval=analysis_interval) + +alive_callback = AliveCallback(analysis_interval=analysis_interval) + +save_solution = SaveSolutionCallback(interval=100, + save_initial_solution=true, + save_final_solution=true, + solution_variables=cons2prim) + +stepsize_callback = StepsizeCallback(cfl=1.0) + +callbacks = CallbackSet(summary_callback, + analysis_callback, alive_callback, + save_solution, + stepsize_callback) + +############################################################################### +# run the simulation + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + maxiters=1.0e7, + 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 diff --git a/src/TrixiAtmo.jl b/src/TrixiAtmo.jl index 84eac18..4bdc567 100644 --- a/src/TrixiAtmo.jl +++ b/src/TrixiAtmo.jl @@ -22,4 +22,6 @@ baz() = Trixi.examples_dir() include("equations/equations.jl") +export CompressibleMoistEulerEquations2D + end # module TrixiAtmo diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl new file mode 100644 index 0000000..408ac5d --- /dev/null +++ b/src/equations/compressible_moist_euler_2d.jl @@ -0,0 +1,1021 @@ +using Trixi +using Trixi: ln_mean, inv_ln_mean +import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, + cons2prim, cons2entropy, max_abs_speed_naive, max_abs_speeds, + entropy, energy_total, flux + +# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). +# Since these FMAs can increase the performance of many numerical algorithms, +# we need to opt-in explicitly. +# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. +@muladd begin + + +struct CompressibleMoistEulerEquations2D{RealT<:Real} <: AbstractCompressibleMoistEulerEquations{2, 6} + p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) + c_pd::RealT # dry air constant + c_vd::RealT # dry air constant + R_d::RealT # dry air gas constant + c_pv::RealT # moist air constant + c_vv::RealT # moist air constant + R_v::RealT # moist air gas constant + c_pl::RealT # liqid water constant + g::RealT # gravitation constant + kappa::RealT # ratio of the gas constand R_d + gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications + L_00::RealT # latent heat of evaporation at 0 K + a::RealT +end + +function CompressibleMoistEulerEquations2D(;g= 9.81, RealT=Float64) + p_0 = 100000.0 + c_pd = 1004.0 + c_vd = 717.0 + R_d = c_pd-c_vd + c_pv = 1885.0 + c_vv = 1424.0 + R_v = c_pv-c_vv + c_pl = 4186.0 + gamma = c_pd / c_vd # = 1/(1 - kappa) + kappa = 1 - inv(gamma) + L_00 = 3147620.0 + a = 360.0 + return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, R_v, c_pl, g, kappa, gamma, L_00, a) + end + + + + +# Calculate 1D flux for a single point. +@inline function flux(u, orientation::Integer, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + f4 = (rho_E + p) * v1 + f5 = rho_v1 * qv + f6 = rho_v1 * ql + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + f4 = (rho_E + p) * v2 + f5 = rho_v2 * qv + f6 = rho_v2 * ql + end + return SVector(f1, f2, f3, f4, f5, f6) +end + +# Calculate 1D flux for a single point in the normal direction. +# Note, this directional vector is not normalized. +@inline function flux(u, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = (rho_v_normal ) * v1 + p * normal_direction[1] + f3 = (rho_v_normal ) * v2 + p * normal_direction[2] + f4 = (rho_e + p) * v_normal + f5 = rho_v_normal * qv + f6 = (rho_v_normal ) * ql + return SVector(f1, f2, f3, f4, f5, f6) +end + +# Slip-wall boundary condition +# Determine the boundary numerical surface flux for a slip wall condition. +# Imposes a zero normal velocity at the wall. +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, x, t, + surface_flux_function, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + + # compute the primitive variables + rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, equations) + qd_local = 1 - qv_local - ql_local + gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl ) * inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Pratical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed + p_star = p_local * (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * inv(gamma-1)) + else # v_normal > 0.0 + A = 2.0 / ((gamma + 1) * rho_local) + B = p_local * (gamma - 1) / (gamma + 1) + p_star = p_local + 0.5 * v_normal / A * (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) + end + + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2], + zero(eltype(u_inner)), + zero(eltype(u_inner)), + zero(eltype(u_inner))) * norm_ +end + +# Fix sign for structured mesh. +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, direction, x, t, + surface_flux_function, equations::CompressibleMoistEulerEquations2D) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, equations) + end + + return boundary_flux +end + +# Rotate momentum flux. The same as in compressible Euler. +@inline function rotate_to_x(u, normal_vector::AbstractVector, equations::CompressibleMoistEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 n_2 0; + # 0 t_1 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3], + u[4], u[5], u[6]) +end + + +# Recreates the convergence test initial condition from compressible euler 2D. +function initial_condition_convergence_test_dry(x, t, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 0 + ql = 0 + + mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) + + T = (ini - 1) / c_vd + E = (mu*T + qv*L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) +end + +# Recreates the convergence test source term from compressible euler 2D. +@inline function source_terms_convergence_test_dry(u, x, t, equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + + qv = 0 + ql = 0 + mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + + T = (rho - 1) / c_vd + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) + + + du1 += rho_x + du2 += rho_x + dp + du3 += du2 + du4 += dE + 2*dp + du5 += qv * du1 + du6 += ql * du1 + + return SVector(du1, du2, du3, du4, du5, du6) +end + +# Initial condition for the convergence analysis. +# Extends the convergence test to the system with gravity and phase change. +function initial_condition_convergence_test_moist(x, t, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 100/L_00 + ql = qv / 10 + + mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) + + T = (ini - 1) /mu + 10/c_vd + 40 + E = (mu*T + qv*L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) +end + +# Source term for the convergence analysis. +# Extends the convergence test to the system with gravity and phase change. +@inline function source_terms_convergence_test_moist(u, x, t, equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1/L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + qv = 100/L_00 + ql = qv / 10 + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + T = (rho - 1) /mu + 10/c_vd + 40 + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + + #Calculate Error in Sources with exact solution and u + u_exact = SVector(rho, rho, rho, rho*E, rho*qv, rho*ql) + + du1, du2, du3, du4, du5, du6 = ( source_terms_moist_bubble(u, x, t, equations) - + source_terms_moist_bubble(u_exact, x, t, equations)) + #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1 += rho_x + du2 += rho_x + dp + du3 += rho_x + dp + du4 += dE + 2*dp + du5 += qv * rho_x + du6 += ql * rho_x + + return SVector(du1, du2, du3, du4, du5, du6) +end + +# Gravity source term +@inline function source_terms_geopotential(u, x, t, equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 + + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) +end + + +@inline function source_terms_geopotential(u, equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 + + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) +end + +# Raylight damping sponge source term form A. Sridhar et al., +#Large-eddy simulations with ClimateMachine: a new open-sourcecode for +#atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, +#https://arxiv.org/abs/2110.00853 [physics.ao-ph] . +@inline function source_terms_nonhydrostatic_raylight_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + z = x[2] + + # relaxed background velocity + vr1, vr2 = (10.0, 0.0) + # damping threshold + z_s = 9000.0 + # boundary top + z_top = 16000.0 + # positive even power with default value 2 + gamma = 2.0 + #relaxation coefficient > 0 + alpha = 0.5 + + tau_s = zero(eltype(u)) + if z > z_s + tau_s = alpha * sin(0.5 * (z-z_s) * inv(z_top - z_s))^(gamma) + end + + return SVector(zero(eltype(u)), + -tau_s * rho *(v1-vr1), + -tau_s * rho *(v2-vr2), + zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) +end + + +# Source term with gravity and phase change +@inline function source_terms_moist_bubble(u, x, t, equations::CompressibleMoistEulerEquations2D) + + return source_terms_geopotential(u, equations) + source_terms_phase_change(u, equations) +end + + +# Calculate pressure and temperature from a state u. +@inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # Inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T + + return SVector(p, T) +end + + +# Calculate Q_ph for a state u. +# This source term models the phase chance between could water and vapor. +@inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, _ , _, _, rho_qv, rho_ql = u + _, T = get_current_condition(u, equations) + rho_qd = rho - rho_qv - rho_ql + + T_C = T - 273.15 + # saturation vapor pressure + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + + # saturation density of vapor + rho_star_qv = p_vs / (R_v * T) + + # Fisher-Burgmeister-Function + a = rho_star_qv - rho_qv + b = rho - rho_qv - rho_qd + + # saturation control factor + # < 1: stronger saturation effect + # > 1: weaker saturation effect + C = 1 + + return (a + b - sqrt(a^2 + b^2)) * C +end + + +# Add the source containing Q_ph +@inline function source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) + + Q_ph = phase_change_term(u, equations) + + return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), + zero(eltype(u)) , Q_ph, -Q_ph) +end + + +# Low Mach number approximate Riemann solver (LMARS) from +# X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. +# Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian +# Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, +# https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. +@inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector ,equations::CompressibleMoistEulerEquations2D) + @unpack a = equations + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr + p_ll, T_ll = get_current_condition(u_ll, equations) + p_rr, T_rr = get_current_condition(u_rr, equations) + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + + v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # diffusion parameter <= 1 + beta = 1 + + # Compute the necessary interface flux components + norm_ = norm(normal_direction) + + rho = 0.5 * (rho_ll + rho_rr) + p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ + v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ + + if (v_interface > 0) + f1, f2, f3, f4, f5, f6 = u_ll * v_interface + f4 += p_ll * v_interface + else + f1, f2, f3, f4, f5, f6 = u_rr * v_interface + f4 += p_rr * v_interface + end + + return SVector(f1, + f2 + p_interface * normal_direction[1], + f3 + p_interface * normal_direction[2], + f4, f5, f6) +end + + +# Convert conservative variables to primitive. +@inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = get_current_condition(u, equations)[1] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, p, qv, ql) +end + + +# Convert conservative variables to primitive with +# temperature instead of pressure. +@inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + T = get_current_condition(u, equations)[2] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, T, qv, ql) +end + +# Convert conservative variables to entropy +@inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p, T = get_current_condition(u, equations) + v_square = v1^2 + v2^2 + rho_qd =rho-rho_qv-rho_ql + + # Work around if an individual density is zero + # Thermodynamic entropy + s_d = 0 + s_v = 0 + s_l = 0 + + # Thermodynamic entropy + if(rho_qd > 0.0) + s_d = c_pd*log(T) - R_d*log(rho_qd*R_d*T) + end + if(rho_qv > 0.0) + s_v = c_pv*log(T) - R_v*log(rho_qv*R_v*T) + end + if(rho_ql > 0.0) + s_l = c_pl*log(T) + end + + g_d = (c_pd - s_d)*T + g_v = L_00 + (c_pv - s_v)*T + g_l = (c_pl - s_l)*T + + + w1 = g_d - 0.5 * v_square + w2 = v1 + w3 = v2 + w4 = -1 + w5 = g_v-g_d + w6 = g_l-g_d + + return inv(T) * SVector(w1, w2, w3, w4, w5, w6) +end + + +# Convert primitive to conservative variables. +@inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) + rho, v1, v2, p, qv, ql = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_qv = rho * qv + rho_ql = rho * ql + rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) + return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) +end + + +# Convert conservative variables to primitive with +# potential temperature instead of pressure. +@inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = dry_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + + +# Convert conservative variables to primitive with +# moist potential temperature instead of pressure. +@inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = moist_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + + +# Convert conservative variables to moisture related variables. +@inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v * inv(p_vs) + + rho_d = rho - (rho_qv + rho_ql) + r_v = inv(rho_d) * rho_qv + r_l = inv(rho_d) * rho_ql + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + + pot1 = qv + pot2 = ql + pot3 = r_v + r_l + pot4 = T + pot5 = H + pot6 = aequivalent_pottemp_thermodynamic(u, equations) + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + + +@inline function density(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + return rho +end + + +@inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) + rho_qd = u[1] - (u[5] + u[6]) + return rho_qd +end + + +@inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho_qv = u[5] + return rho_qv +end + + +@inline function temperature(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + return T +end + + +@inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + T = get_current_condition(u, equations)[2] + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + return H +end + + +@inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho_ql = u[6] + return rho_ql +end + + +@inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_ql = u[6] + ql = rho_ql / rho + return ql +end + + +@inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_qv = u[5] + qv = rho_qv / rho + return qv +end + + +@inline function pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T + return p +end + + +@inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_times_p = rho * get_current_condition(u, equations)[1] + return rho_times_p +end + + +# Calculate thermodynamic entropy for a conservative state `cons`. +# This is the dryspecific entropy multiplied by the dry air density. +@inline function entropy_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, c_vv, c_pl, R_d, R_v = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_qd =cons[1]-cons[5]-cons[6] + rho_qv =cons[5] + rho_ql =cons[6] + # Thermodynamic entropy + s_d = c_vd*log(T) - R_d*log(rho_qd*R_d) + + s_v = c_vv*log(T) - R_v*log(rho_qv*R_v) + + s_l = c_pl*log(T) + + return rho_qd*s_d + rho_qv*s_v + rho_ql*s_l +end + + +# Calculate mathematical entropy for a conservative state `cons`. +@inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) + + return S +end + + +# Default entropy is the mathematical entropy. +@inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, equations) + + +# Calculate total energy for a conservative state `cons`. +@inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] + + +# Calculate kinetic energy for a conservative state `cons`. +@inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) +end + + +# Calculate internal energy for a conservative state `cons`. +@inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) + return energy_total(cons, equations) - energy_kinetic(cons, equations) +end + + +# Calculate the dry potential temperature for a conservative state `cons`. +@inline function dry_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, p_0, kappa = equations + # Pressure + p = get_current_condition(cons, equations)[1] + # Potential temperature + pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) + + return pot +end + + +# Calculate the moist potential temperature for a conservative state `cons`. +@inline function moist_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_d = cons[1] - (cons[5] + cons[6]) + r_v = inv(rho_d) * cons[5] + r_l = inv(rho_d) * cons[6] + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + return pot +end + + +# Calculate the aequivalent potential temperature for a conservative state `cons`. +@inline function aequivalent_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + #Aequivalentpotential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(- r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + return aeq_pot +end + + +# Convert conservative variables to primitive varuables with +# aequivalent potential temperature instead of pressure +# and mixing ratios innstead of specific contend. +@inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + #Aequivalentpotential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(- r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = aeq_pot + pot5 = r_v + pot6 = r_t + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + + +@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) + + + # Compute the sound speeds on the left and right + v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) +end + + +# Adjusted version of LLF dissipation from compressible euler. +@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll *c_pl) * inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll *c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr *c_pl) * inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr *c_pl) + # Calculate normal velocities and sound speed + # left + v_ll = ( v1_ll * normal_direction[1] + + v2_ll * normal_direction[2] ) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + # right + v_rr = ( v1_rr * normal_direction[1] + + v2_rr * normal_direction[2] ) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) +end + + +# Adjusted version of lambda_max from compressible euler. +@inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho, v1, v2, p, qv, ql = cons2prim(u, equations) + qd = 1 - qv - ql + + gamma = (qd * c_pd + qv * c_pv + ql * c_pl ) * inv(qd * c_vd + qv * c_vv + ql * c_pl) + c = sqrt(gamma * p / rho) + + return (abs(v1) + c, abs(v2) + c) +end + + +# Adjusted EC flux in a normal direction with R_q=0. This is based on +# A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the +# multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, +# https://arxiv.org/abs/1904.00972 [math.NA]. +@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + R_q = 0 + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr + + rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll + rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr + v1_ll = rho_v1_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_ll = rho_v2_ll / rho_ll + v2_rr = rho_v2_rr / rho_rr + + # inner energy + rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) + rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) + + # Absolute Temperature + T_ll = (rho_e_ll - L_00 * rho_qv_ll) / (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) + T_rr = (rho_e_rr - L_00 * rho_qv_rr) / (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) + + # Compute the necessary mean values + rho_qd_mean = 0 + rho_qv_mean = 0 + rho_ql_mean = 0 + inv_T_mean = 0 + if(!(rho_qd_ll==0.0) && !(rho_qd_rr==0.0)) + rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) + end + if(!(rho_qv_ll==0.0) && !(rho_qv_rr==0.0)) + rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) + end + if(!(rho_ql_ll==0.0) && !(rho_ql_rr==0.0)) + rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) + end + if(!(inv(T_ll)==0.0) && !(inv(T_rr)==0.0)) + inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) + end + + + + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) + v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) + rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) + rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) + rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) + inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) + v_dot_n_avg = normal_direction[1]*v1_avg + normal_direction[2]*v2_avg + + p_int = inv(inv_T_avg) * (R_d*rho_qd_avg + R_v*rho_qv_avg + R_q*rho_ql_avg) + K_avg = 0.5 *(v1_square_avg + v2_square_avg) + + f_1d = rho_qd_mean * v_dot_n_avg + f_1v = rho_qv_mean * v_dot_n_avg + f_1l = rho_ql_mean * v_dot_n_avg + f1 = f_1d + f_1v + f_1l + f2 = f1*v1_avg + normal_direction[1]*p_int + f3 = f1*v2_avg + normal_direction[2]*p_int + f4 = ((c_vd*inv_T_mean - K_avg) * f_1d + (L_00 + c_vv*inv_T_mean - K_avg) * f_1v + + (c_pl*inv_T_mean - K_avg) * f_1l + v1_avg*f2 + v2_avg*f3 ) + + return SVector(f1, f2, f3, f4, f_1v, f_1l) +end + +varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", "rho_v2", "rho_E", "rho_qv", "rho_ql") +varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "p", "qv", "ql") +varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "T", "qv", "ql") +varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "drypottemp", "qv", "ql") +varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "moistpottemp", "qv", "ql") +varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", "T", "H", "aeqpottemp") +varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "aeqpottemp", "rv", "rt") + +end # @muladd + diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 8b13789..88da0e6 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -1 +1,5 @@ +using Trixi: AbstractEquations + +abstract type AbstractCompressibleMoistEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end +include("compressible_moist_euler_2d.jl") From 5a5c16174f9f3d40d4cf82056e08da0f04d6d58b Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 09:45:48 +0530 Subject: [PATCH 02/17] Format --- src/equations/compressible_moist_euler_2d.jl | 2026 +++++++++--------- src/equations/equations.jl | 4 +- 2 files changed, 1019 insertions(+), 1011 deletions(-) diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl index 408ac5d..a15a10e 100644 --- a/src/equations/compressible_moist_euler_2d.jl +++ b/src/equations/compressible_moist_euler_2d.jl @@ -9,1013 +9,1021 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, # we need to opt-in explicitly. # See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin - - -struct CompressibleMoistEulerEquations2D{RealT<:Real} <: AbstractCompressibleMoistEulerEquations{2, 6} - p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) - c_pd::RealT # dry air constant - c_vd::RealT # dry air constant - R_d::RealT # dry air gas constant - c_pv::RealT # moist air constant - c_vv::RealT # moist air constant - R_v::RealT # moist air gas constant - c_pl::RealT # liqid water constant - g::RealT # gravitation constant - kappa::RealT # ratio of the gas constand R_d - gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications - L_00::RealT # latent heat of evaporation at 0 K - a::RealT -end - -function CompressibleMoistEulerEquations2D(;g= 9.81, RealT=Float64) - p_0 = 100000.0 - c_pd = 1004.0 - c_vd = 717.0 - R_d = c_pd-c_vd - c_pv = 1885.0 - c_vv = 1424.0 - R_v = c_pv-c_vv - c_pl = 4186.0 - gamma = c_pd / c_vd # = 1/(1 - kappa) - kappa = 1 - inv(gamma) - L_00 = 3147620.0 - a = 360.0 - return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, R_v, c_pl, g, kappa, gamma, L_00, a) - end - - - - -# Calculate 1D flux for a single point. -@inline function flux(u, orientation::Integer, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - f4 = (rho_E + p) * v1 - f5 = rho_v1 * qv - f6 = rho_v1 * ql - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - f4 = (rho_E + p) * v2 - f5 = rho_v2 * qv - f6 = rho_v2 * ql - end - return SVector(f1, f2, f3, f4, f5, f6) -end - -# Calculate 1D flux for a single point in the normal direction. -# Note, this directional vector is not normalized. -@inline function flux(u, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = (rho_v_normal ) * v1 + p * normal_direction[1] - f3 = (rho_v_normal ) * v2 + p * normal_direction[2] - f4 = (rho_e + p) * v_normal - f5 = rho_v_normal * qv - f6 = (rho_v_normal ) * ql - return SVector(f1, f2, f3, f4, f5, f6) -end - -# Slip-wall boundary condition -# Determine the boundary numerical surface flux for a slip wall condition. -# Imposes a zero normal velocity at the wall. -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, x, t, - surface_flux_function, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - - # compute the primitive variables - rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, equations) - qd_local = 1 - qv_local - ql_local - gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl ) * inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Pratical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed - p_star = p_local * (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * inv(gamma-1)) - else # v_normal > 0.0 - A = 2.0 / ((gamma + 1) * rho_local) - B = p_local * (gamma - 1) / (gamma + 1) - p_star = p_local + 0.5 * v_normal / A * (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) - end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2], - zero(eltype(u_inner)), - zero(eltype(u_inner)), - zero(eltype(u_inner))) * norm_ -end - -# Fix sign for structured mesh. -@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, direction, x, t, - surface_flux_function, equations::CompressibleMoistEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, equations) - end - - return boundary_flux -end - -# Rotate momentum flux. The same as in compressible Euler. -@inline function rotate_to_x(u, normal_vector::AbstractVector, equations::CompressibleMoistEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 n_2 0; - # 0 t_1 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3], - u[4], u[5], u[6]) -end - - -# Recreates the convergence test initial condition from compressible euler 2D. -function initial_condition_convergence_test_dry(x, t, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1/L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 0 - ql = 0 - - mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) - - T = (ini - 1) / c_vd - E = (mu*T + qv*L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) -end - -# Recreates the convergence test source term from compressible euler 2D. -@inline function source_terms_convergence_test_dry(u, x, t, equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1/L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - - qv = 0 - ql = 0 - mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - - T = (rho - 1) / c_vd - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) - - - du1 += rho_x - du2 += rho_x + dp - du3 += du2 - du4 += dE + 2*dp - du5 += qv * du1 - du6 += ql * du1 - - return SVector(du1, du2, du3, du4, du5, du6) -end - -# Initial condition for the convergence analysis. -# Extends the convergence test to the system with gravity and phase change. -function initial_condition_convergence_test_moist(x, t, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1/L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 100/L_00 - ql = qv / 10 - - mu = ((1 - qv - ql)*c_vd + qv*c_vv + ql*c_pl) - - T = (ini - 1) /mu + 10/c_vd + 40 - E = (mu*T + qv*L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) -end - -# Source term for the convergence analysis. -# Extends the convergence test to the system with gravity and phase change. -@inline function source_terms_convergence_test_moist(u, x, t, equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1/L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - qv = 100/L_00 - ql = qv / 10 - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - T = (rho - 1) /mu + 10/c_vd + 40 - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - - #Calculate Error in Sources with exact solution and u - u_exact = SVector(rho, rho, rho, rho*E, rho*qv, rho*ql) - - du1, du2, du3, du4, du5, du6 = ( source_terms_moist_bubble(u, x, t, equations) - - source_terms_moist_bubble(u_exact, x, t, equations)) - #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1 += rho_x - du2 += rho_x + dp - du3 += rho_x + dp - du4 += dE + 2*dp - du5 += qv * rho_x - du6 += ql * rho_x - - return SVector(du1, du2, du3, du4, du5, du6) -end - -# Gravity source term -@inline function source_terms_geopotential(u, x, t, equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) -end - - -@inline function source_terms_geopotential(u, equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) -end - -# Raylight damping sponge source term form A. Sridhar et al., -#Large-eddy simulations with ClimateMachine: a new open-sourcecode for -#atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, -#https://arxiv.org/abs/2110.00853 [physics.ao-ph] . -@inline function source_terms_nonhydrostatic_raylight_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - z = x[2] - - # relaxed background velocity - vr1, vr2 = (10.0, 0.0) - # damping threshold - z_s = 9000.0 - # boundary top - z_top = 16000.0 - # positive even power with default value 2 - gamma = 2.0 - #relaxation coefficient > 0 - alpha = 0.5 - - tau_s = zero(eltype(u)) - if z > z_s - tau_s = alpha * sin(0.5 * (z-z_s) * inv(z_top - z_s))^(gamma) - end - - return SVector(zero(eltype(u)), - -tau_s * rho *(v1-vr1), - -tau_s * rho *(v2-vr2), - zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) -end - - -# Source term with gravity and phase change -@inline function source_terms_moist_bubble(u, x, t, equations::CompressibleMoistEulerEquations2D) - - return source_terms_geopotential(u, equations) + source_terms_phase_change(u, equations) -end - - -# Calculate pressure and temperature from a state u. -@inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # Inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - - return SVector(p, T) -end - - -# Calculate Q_ph for a state u. -# This source term models the phase chance between could water and vapor. -@inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, _ , _, _, rho_qv, rho_ql = u - _, T = get_current_condition(u, equations) - rho_qd = rho - rho_qv - rho_ql - - T_C = T - 273.15 - # saturation vapor pressure - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - - # saturation density of vapor - rho_star_qv = p_vs / (R_v * T) - - # Fisher-Burgmeister-Function - a = rho_star_qv - rho_qv - b = rho - rho_qv - rho_qd - - # saturation control factor - # < 1: stronger saturation effect - # > 1: weaker saturation effect - C = 1 - - return (a + b - sqrt(a^2 + b^2)) * C -end - - -# Add the source containing Q_ph -@inline function source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) - - Q_ph = phase_change_term(u, equations) - - return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), - zero(eltype(u)) , Q_ph, -Q_ph) -end - - -# Low Mach number approximate Riemann solver (LMARS) from -# X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. -# Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian -# Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, -# https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. -@inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector ,equations::CompressibleMoistEulerEquations2D) - @unpack a = equations - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr - p_ll, T_ll = get_current_condition(u_ll, equations) - p_rr, T_rr = get_current_condition(u_rr, equations) - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - - v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # diffusion parameter <= 1 - beta = 1 - - # Compute the necessary interface flux components - norm_ = norm(normal_direction) - - rho = 0.5 * (rho_ll + rho_rr) - p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ - v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ - - if (v_interface > 0) - f1, f2, f3, f4, f5, f6 = u_ll * v_interface - f4 += p_ll * v_interface - else - f1, f2, f3, f4, f5, f6 = u_rr * v_interface - f4 += p_rr * v_interface - end - - return SVector(f1, - f2 + p_interface * normal_direction[1], - f3 + p_interface * normal_direction[2], - f4, f5, f6) -end - - -# Convert conservative variables to primitive. -@inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = get_current_condition(u, equations)[1] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, p, qv, ql) -end - - -# Convert conservative variables to primitive with -# temperature instead of pressure. -@inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - T = get_current_condition(u, equations)[2] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, T, qv, ql) -end - -# Convert conservative variables to entropy -@inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p, T = get_current_condition(u, equations) - v_square = v1^2 + v2^2 - rho_qd =rho-rho_qv-rho_ql - - # Work around if an individual density is zero - # Thermodynamic entropy - s_d = 0 - s_v = 0 - s_l = 0 - - # Thermodynamic entropy - if(rho_qd > 0.0) - s_d = c_pd*log(T) - R_d*log(rho_qd*R_d*T) - end - if(rho_qv > 0.0) - s_v = c_pv*log(T) - R_v*log(rho_qv*R_v*T) - end - if(rho_ql > 0.0) - s_l = c_pl*log(T) - end - - g_d = (c_pd - s_d)*T - g_v = L_00 + (c_pv - s_v)*T - g_l = (c_pl - s_l)*T - - - w1 = g_d - 0.5 * v_square - w2 = v1 - w3 = v2 - w4 = -1 - w5 = g_v-g_d - w6 = g_l-g_d - - return inv(T) * SVector(w1, w2, w3, w4, w5, w6) -end - - -# Convert primitive to conservative variables. -@inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) - rho, v1, v2, p, qv, ql = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_qv = rho * qv - rho_ql = rho * ql - rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) - return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) -end - - -# Convert conservative variables to primitive with -# potential temperature instead of pressure. -@inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = dry_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) -end - - -# Convert conservative variables to primitive with -# moist potential temperature instead of pressure. -@inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = moist_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) -end - - -# Convert conservative variables to moisture related variables. -@inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v * inv(p_vs) - - rho_d = rho - (rho_qv + rho_ql) - r_v = inv(rho_d) * rho_qv - r_l = inv(rho_d) * rho_ql - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - - pot1 = qv - pot2 = ql - pot3 = r_v + r_l - pot4 = T - pot5 = H - pot6 = aequivalent_pottemp_thermodynamic(u, equations) - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) -end - - -@inline function density(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - return rho -end - - -@inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) - rho_qd = u[1] - (u[5] + u[6]) - return rho_qd -end - - -@inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho_qv = u[5] - return rho_qv -end - - -@inline function temperature(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - return T -end - - -@inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - T = get_current_condition(u, equations)[2] - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - return H -end - - -@inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho_ql = u[6] - return rho_ql -end - - -@inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_ql = u[6] - ql = rho_ql / rho - return ql -end - - -@inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_qv = u[5] - qv = rho_qv / rho - return qv -end - - -@inline function pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - return p -end - - -@inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_times_p = rho * get_current_condition(u, equations)[1] - return rho_times_p -end - - -# Calculate thermodynamic entropy for a conservative state `cons`. -# This is the dryspecific entropy multiplied by the dry air density. -@inline function entropy_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, c_vv, c_pl, R_d, R_v = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_qd =cons[1]-cons[5]-cons[6] - rho_qv =cons[5] - rho_ql =cons[6] - # Thermodynamic entropy - s_d = c_vd*log(T) - R_d*log(rho_qd*R_d) - - s_v = c_vv*log(T) - R_v*log(rho_qv*R_v) - - s_l = c_pl*log(T) - - return rho_qd*s_d + rho_qv*s_v + rho_ql*s_l -end - - -# Calculate mathematical entropy for a conservative state `cons`. -@inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) - - return S -end - - -# Default entropy is the mathematical entropy. -@inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, equations) - - -# Calculate total energy for a conservative state `cons`. -@inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] - - -# Calculate kinetic energy for a conservative state `cons`. -@inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) -end - - -# Calculate internal energy for a conservative state `cons`. -@inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) - return energy_total(cons, equations) - energy_kinetic(cons, equations) -end - - -# Calculate the dry potential temperature for a conservative state `cons`. -@inline function dry_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, p_0, kappa = equations - # Pressure - p = get_current_condition(cons, equations)[1] - # Potential temperature - pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) - - return pot -end - - -# Calculate the moist potential temperature for a conservative state `cons`. -@inline function moist_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_d = cons[1] - (cons[5] + cons[6]) - r_v = inv(rho_d) * cons[5] - r_l = inv(rho_d) * cons[6] - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - return pot -end - - -# Calculate the aequivalent potential temperature for a conservative state `cons`. -@inline function aequivalent_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - #Aequivalentpotential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(- r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - return aeq_pot -end - - -# Convert conservative variables to primitive varuables with -# aequivalent potential temperature instead of pressure -# and mixing ratios innstead of specific contend. -@inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - #Aequivalentpotential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(- r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = aeq_pot - pot5 = r_v - pot6 = r_t - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) -end - - -@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) - - - # Compute the sound speeds on the left and right - v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) -end - - -# Adjusted version of LLF dissipation from compressible euler. -@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll *c_pl) * inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll *c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr *c_pl) * inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr *c_pl) - # Calculate normal velocities and sound speed - # left - v_ll = ( v1_ll * normal_direction[1] - + v2_ll * normal_direction[2] ) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - # right - v_rr = ( v1_rr * normal_direction[1] - + v2_rr * normal_direction[2] ) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) -end - - -# Adjusted version of lambda_max from compressible euler. -@inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho, v1, v2, p, qv, ql = cons2prim(u, equations) - qd = 1 - qv - ql - - gamma = (qd * c_pd + qv * c_pv + ql * c_pl ) * inv(qd * c_vd + qv * c_vv + ql * c_pl) - c = sqrt(gamma * p / rho) - - return (abs(v1) + c, abs(v2) + c) -end - - -# Adjusted EC flux in a normal direction with R_q=0. This is based on -# A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the -# multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, -# https://arxiv.org/abs/1904.00972 [math.NA]. -@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - R_q = 0 - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr - - rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll - rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr - v1_ll = rho_v1_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_ll = rho_v2_ll / rho_ll - v2_rr = rho_v2_rr / rho_rr - - # inner energy - rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) - rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) - - # Absolute Temperature - T_ll = (rho_e_ll - L_00 * rho_qv_ll) / (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) - T_rr = (rho_e_rr - L_00 * rho_qv_rr) / (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) - - # Compute the necessary mean values - rho_qd_mean = 0 - rho_qv_mean = 0 - rho_ql_mean = 0 - inv_T_mean = 0 - if(!(rho_qd_ll==0.0) && !(rho_qd_rr==0.0)) - rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) - end - if(!(rho_qv_ll==0.0) && !(rho_qv_rr==0.0)) - rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) - end - if(!(rho_ql_ll==0.0) && !(rho_ql_rr==0.0)) - rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) - end - if(!(inv(T_ll)==0.0) && !(inv(T_rr)==0.0)) - inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) - end - - - - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) - v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) - rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) - rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) - rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) - inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) - v_dot_n_avg = normal_direction[1]*v1_avg + normal_direction[2]*v2_avg - - p_int = inv(inv_T_avg) * (R_d*rho_qd_avg + R_v*rho_qv_avg + R_q*rho_ql_avg) - K_avg = 0.5 *(v1_square_avg + v2_square_avg) - - f_1d = rho_qd_mean * v_dot_n_avg - f_1v = rho_qv_mean * v_dot_n_avg - f_1l = rho_ql_mean * v_dot_n_avg - f1 = f_1d + f_1v + f_1l - f2 = f1*v1_avg + normal_direction[1]*p_int - f3 = f1*v2_avg + normal_direction[2]*p_int - f4 = ((c_vd*inv_T_mean - K_avg) * f_1d + (L_00 + c_vv*inv_T_mean - K_avg) * f_1v + - (c_pl*inv_T_mean - K_avg) * f_1l + v1_avg*f2 + v2_avg*f3 ) - - return SVector(f1, f2, f3, f4, f_1v, f_1l) -end - -varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", "rho_v2", "rho_E", "rho_qv", "rho_ql") -varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "p", "qv", "ql") -varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "T", "qv", "ql") -varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "drypottemp", "qv", "ql") -varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "moistpottemp", "qv", "ql") -varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", "T", "H", "aeqpottemp") -varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", "aeqpottemp", "rv", "rt") - + struct CompressibleMoistEulerEquations2D{RealT <: Real} <: + AbstractCompressibleMoistEulerEquations{2, 6} + p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) + c_pd::RealT # dry air constant + c_vd::RealT # dry air constant + R_d::RealT # dry air gas constant + c_pv::RealT # moist air constant + c_vv::RealT # moist air constant + R_v::RealT # moist air gas constant + c_pl::RealT # liqid water constant + g::RealT # gravitation constant + kappa::RealT # ratio of the gas constand R_d + gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications + L_00::RealT # latent heat of evaporation at 0 K + a::RealT + end + + function CompressibleMoistEulerEquations2D(; g = 9.81, RealT = Float64) + p_0 = 100000.0 + c_pd = 1004.0 + c_vd = 717.0 + R_d = c_pd - c_vd + c_pv = 1885.0 + c_vv = 1424.0 + R_v = c_pv - c_vv + c_pl = 4186.0 + gamma = c_pd / c_vd # = 1/(1 - kappa) + kappa = 1 - inv(gamma) + L_00 = 3147620.0 + a = 360.0 + return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, + R_v, c_pl, g, kappa, gamma, L_00, a) + end + + # Calculate 1D flux for a single point. + @inline function flux(u, orientation::Integer, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + f4 = (rho_E + p) * v1 + f5 = rho_v1 * qv + f6 = rho_v1 * ql + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + f4 = (rho_E + p) * v2 + f5 = rho_v2 * qv + f6 = rho_v2 * ql + end + return SVector(f1, f2, f3, f4, f5, f6) + end + + # Calculate 1D flux for a single point in the normal direction. + # Note, this directional vector is not normalized. + @inline function flux(u, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = (rho_v_normal) * v1 + p * normal_direction[1] + f3 = (rho_v_normal) * v2 + p * normal_direction[2] + f4 = (rho_e + p) * v_normal + f5 = rho_v_normal * qv + f6 = (rho_v_normal) * ql + return SVector(f1, f2, f3, f4, f5, f6) + end + + # Slip-wall boundary condition + # Determine the boundary numerical surface flux for a slip wall condition. + # Imposes a zero normal velocity at the wall. + @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + x, t, + surface_flux_function, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + + # compute the primitive variables + rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, + equations) + qd_local = 1 - qv_local - ql_local + gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl) * + inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Pratical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed + p_star = p_local * + (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * + inv(gamma - 1)) + else # v_normal > 0.0 + A = 2.0 / ((gamma + 1) * rho_local) + B = p_local * (gamma - 1) / (gamma + 1) + p_star = p_local + + 0.5 * v_normal / A * + (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) + end + + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2], + zero(eltype(u_inner)), + zero(eltype(u_inner)), + zero(eltype(u_inner))) * norm_ + end + + # Fix sign for structured mesh. + @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + direction, x, t, + surface_flux_function, + equations::CompressibleMoistEulerEquations2D) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, + equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, + equations) + end + + return boundary_flux + end + + # Rotate momentum flux. The same as in compressible Euler. + @inline function rotate_to_x(u, normal_vector::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 n_2 0; + # 0 t_1 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3], + u[4], u[5], u[6]) + end + + # Recreates the convergence test initial condition from compressible euler 2D. + function initial_condition_convergence_test_dry(x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 0 + ql = 0 + + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + + T = (ini - 1) / c_vd + E = (mu * T + qv * L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) + end + + # Recreates the convergence test source term from compressible euler 2D. + @inline function source_terms_convergence_test_dry(u, x, t, + equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + qv = 0 + ql = 0 + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + T = (rho - 1) / c_vd + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) + + du1 += rho_x + du2 += rho_x + dp + du3 += du2 + du4 += dE + 2 * dp + du5 += qv * du1 + du6 += ql * du1 + + return SVector(du1, du2, du3, du4, du5, du6) + end + + # Initial condition for the convergence analysis. + # Extends the convergence test to the system with gravity and phase change. + function initial_condition_convergence_test_moist(x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 100 / L_00 + ql = qv / 10 + + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + + T = (ini - 1) / mu + 10 / c_vd + 40 + E = (mu * T + qv * L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) + end + + # Source term for the convergence analysis. + # Extends the convergence test to the system with gravity and phase change. + @inline function source_terms_convergence_test_moist(u, x, t, + equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + qv = 100 / L_00 + ql = qv / 10 + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + T = (rho - 1) / mu + 10 / c_vd + 40 + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + + #Calculate Error in Sources with exact solution and u + u_exact = SVector(rho, rho, rho, rho * E, rho * qv, rho * ql) + + du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - + source_terms_moist_bubble(u_exact, x, t, equations)) + #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1 += rho_x + du2 += rho_x + dp + du3 += rho_x + dp + du4 += dE + 2 * dp + du5 += qv * rho_x + du6 += ql * rho_x + + return SVector(du1, du2, du3, du4, du5, du6) + end + + # Gravity source term + @inline function source_terms_geopotential(u, x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 + + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) + end + + @inline function source_terms_geopotential(u, + equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 + + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) + end + + # Raylight damping sponge source term form A. Sridhar et al., + #Large-eddy simulations with ClimateMachine: a new open-sourcecode for + #atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, + #https://arxiv.org/abs/2110.00853 [physics.ao-ph] . + @inline function source_terms_nonhydrostatic_raylight_sponge(u, x, t, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + z = x[2] + + # relaxed background velocity + vr1, vr2 = (10.0, 0.0) + # damping threshold + z_s = 9000.0 + # boundary top + z_top = 16000.0 + # positive even power with default value 2 + gamma = 2.0 + #relaxation coefficient > 0 + alpha = 0.5 + + tau_s = zero(eltype(u)) + if z > z_s + tau_s = alpha * sin(0.5 * (z - z_s) * inv(z_top - z_s))^(gamma) + end + + return SVector(zero(eltype(u)), + -tau_s * rho * (v1 - vr1), + -tau_s * rho * (v2 - vr2), + zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) + end + + # Source term with gravity and phase change + @inline function source_terms_moist_bubble(u, x, t, + equations::CompressibleMoistEulerEquations2D) + return source_terms_geopotential(u, equations) + + source_terms_phase_change(u, equations) + end + + # Calculate pressure and temperature from a state u. + @inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # Inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T + + return SVector(p, T) + end + + # Calculate Q_ph for a state u. + # This source term models the phase chance between could water and vapor. + @inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, _, _, _, rho_qv, rho_ql = u + _, T = get_current_condition(u, equations) + rho_qd = rho - rho_qv - rho_ql + + T_C = T - 273.15 + # saturation vapor pressure + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + + # saturation density of vapor + rho_star_qv = p_vs / (R_v * T) + + # Fisher-Burgmeister-Function + a = rho_star_qv - rho_qv + b = rho - rho_qv - rho_qd + + # saturation control factor + # < 1: stronger saturation effect + # > 1: weaker saturation effect + C = 1 + + return (a + b - sqrt(a^2 + b^2)) * C + end + + # Add the source containing Q_ph + @inline function source_terms_phase_change(u, + equations::CompressibleMoistEulerEquations2D) + Q_ph = phase_change_term(u, equations) + + return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), + zero(eltype(u)), Q_ph, -Q_ph) + end + + # Low Mach number approximate Riemann solver (LMARS) from + # X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. + # Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian + # Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, + # https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. + @inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack a = equations + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr + p_ll, T_ll = get_current_condition(u_ll, equations) + p_rr, T_rr = get_current_condition(u_rr, equations) + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + + v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # diffusion parameter <= 1 + beta = 1 + + # Compute the necessary interface flux components + norm_ = norm(normal_direction) + + rho = 0.5 * (rho_ll + rho_rr) + p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ + v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ + + if (v_interface > 0) + f1, f2, f3, f4, f5, f6 = u_ll * v_interface + f4 += p_ll * v_interface + else + f1, f2, f3, f4, f5, f6 = u_rr * v_interface + f4 += p_rr * v_interface + end + + return SVector(f1, + f2 + p_interface * normal_direction[1], + f3 + p_interface * normal_direction[2], + f4, f5, f6) + end + + # Convert conservative variables to primitive. + @inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = get_current_condition(u, equations)[1] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, p, qv, ql) + end + + # Convert conservative variables to primitive with + # temperature instead of pressure. + @inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + T = get_current_condition(u, equations)[2] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, T, qv, ql) + end + + # Convert conservative variables to entropy + @inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p, T = get_current_condition(u, equations) + v_square = v1^2 + v2^2 + rho_qd = rho - rho_qv - rho_ql + + # Work around if an individual density is zero + # Thermodynamic entropy + s_d = 0 + s_v = 0 + s_l = 0 + + # Thermodynamic entropy + if (rho_qd > 0.0) + s_d = c_pd * log(T) - R_d * log(rho_qd * R_d * T) + end + if (rho_qv > 0.0) + s_v = c_pv * log(T) - R_v * log(rho_qv * R_v * T) + end + if (rho_ql > 0.0) + s_l = c_pl * log(T) + end + + g_d = (c_pd - s_d) * T + g_v = L_00 + (c_pv - s_v) * T + g_l = (c_pl - s_l) * T + + w1 = g_d - 0.5 * v_square + w2 = v1 + w3 = v2 + w4 = -1 + w5 = g_v - g_d + w6 = g_l - g_d + + return inv(T) * SVector(w1, w2, w3, w4, w5, w6) + end + + # Convert primitive to conservative variables. + @inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) + rho, v1, v2, p, qv, ql = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_qv = rho * qv + rho_ql = rho * ql + rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) + return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) + end + + # Convert conservative variables to primitive with + # potential temperature instead of pressure. + @inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = dry_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) + end + + # Convert conservative variables to primitive with + # moist potential temperature instead of pressure. + @inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = moist_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) + end + + # Convert conservative variables to moisture related variables. + @inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v * inv(p_vs) + + rho_d = rho - (rho_qv + rho_ql) + r_v = inv(rho_d) * rho_qv + r_l = inv(rho_d) * rho_ql + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + + pot1 = qv + pot2 = ql + pot3 = r_v + r_l + pot4 = T + pot5 = H + pot6 = aequivalent_pottemp_thermodynamic(u, equations) + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) + end + + @inline function density(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + return rho + end + + @inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) + rho_qd = u[1] - (u[5] + u[6]) + return rho_qd + end + + @inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho_qv = u[5] + return rho_qv + end + + @inline function temperature(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + return T + end + + @inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + T = get_current_condition(u, equations)[2] + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + return H + end + + @inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho_ql = u[6] + return rho_ql + end + + @inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_ql = u[6] + ql = rho_ql / rho + return ql + end + + @inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_qv = u[5] + qv = rho_qv / rho + return qv + end + + @inline function pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T + return p + end + + @inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_times_p = rho * get_current_condition(u, equations)[1] + return rho_times_p + end + + # Calculate thermodynamic entropy for a conservative state `cons`. + # This is the dryspecific entropy multiplied by the dry air density. + @inline function entropy_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, c_vv, c_pl, R_d, R_v = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_qd = cons[1] - cons[5] - cons[6] + rho_qv = cons[5] + rho_ql = cons[6] + # Thermodynamic entropy + s_d = c_vd * log(T) - R_d * log(rho_qd * R_d) + + s_v = c_vv * log(T) - R_v * log(rho_qv * R_v) + + s_l = c_pl * log(T) + + return rho_qd * s_d + rho_qv * s_v + rho_ql * s_l + end + + # Calculate mathematical entropy for a conservative state `cons`. + @inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) + + return S + end + + # Default entropy is the mathematical entropy. + @inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, + equations) + + # Calculate total energy for a conservative state `cons`. + @inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] + + # Calculate kinetic energy for a conservative state `cons`. + @inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) + end + + # Calculate internal energy for a conservative state `cons`. + @inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) + return energy_total(cons, equations) - energy_kinetic(cons, equations) + end + + # Calculate the dry potential temperature for a conservative state `cons`. + @inline function dry_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, p_0, kappa = equations + # Pressure + p = get_current_condition(cons, equations)[1] + # Potential temperature + pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) + + return pot + end + + # Calculate the moist potential temperature for a conservative state `cons`. + @inline function moist_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_d = cons[1] - (cons[5] + cons[6]) + r_v = inv(rho_d) * cons[5] + r_l = inv(rho_d) * cons[6] + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + return pot + end + + # Calculate the aequivalent potential temperature for a conservative state `cons`. + @inline function aequivalent_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + #Aequivalentpotential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + return aeq_pot + end + + # Convert conservative variables to primitive varuables with + # aequivalent potential temperature instead of pressure + # and mixing ratios innstead of specific contend. + @inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + #Aequivalentpotential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = aeq_pot + pot5 = r_v + pot6 = r_t + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) + end + + @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * + inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * + inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) + + # Compute the sound speeds on the left and right + v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) + end + + # Adjusted version of LLF dissipation from compressible euler. + @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * + inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * + inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) + # Calculate normal velocities and sound speed + # left + v_ll = (v1_ll * normal_direction[1] + + + v2_ll * normal_direction[2]) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + # right + v_rr = (v1_rr * normal_direction[1] + + + v2_rr * normal_direction[2]) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) + end + + # Adjusted version of lambda_max from compressible euler. + @inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho, v1, v2, p, qv, ql = cons2prim(u, equations) + qd = 1 - qv - ql + + gamma = (qd * c_pd + qv * c_pv + ql * c_pl) * inv(qd * c_vd + qv * c_vv + ql * c_pl) + c = sqrt(gamma * p / rho) + + return (abs(v1) + c, abs(v2) + c) + end + + # Adjusted EC flux in a normal direction with R_q=0. This is based on + # A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the + # multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, + # https://arxiv.org/abs/1904.00972 [math.NA]. + @inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + R_q = 0 + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr + + rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll + rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr + v1_ll = rho_v1_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_ll = rho_v2_ll / rho_ll + v2_rr = rho_v2_rr / rho_rr + + # inner energy + rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) + rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) + + # Absolute Temperature + T_ll = (rho_e_ll - L_00 * rho_qv_ll) / + (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) + T_rr = (rho_e_rr - L_00 * rho_qv_rr) / + (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) + + # Compute the necessary mean values + rho_qd_mean = 0 + rho_qv_mean = 0 + rho_ql_mean = 0 + inv_T_mean = 0 + if (!(rho_qd_ll == 0.0) && !(rho_qd_rr == 0.0)) + rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) + end + if (!(rho_qv_ll == 0.0) && !(rho_qv_rr == 0.0)) + rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) + end + if (!(rho_ql_ll == 0.0) && !(rho_ql_rr == 0.0)) + rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) + end + if (!(inv(T_ll) == 0.0) && !(inv(T_rr) == 0.0)) + inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) + end + + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) + v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) + rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) + rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) + rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) + inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) + v_dot_n_avg = normal_direction[1] * v1_avg + normal_direction[2] * v2_avg + + p_int = inv(inv_T_avg) * (R_d * rho_qd_avg + R_v * rho_qv_avg + R_q * rho_ql_avg) + K_avg = 0.5 * (v1_square_avg + v2_square_avg) + + f_1d = rho_qd_mean * v_dot_n_avg + f_1v = rho_qv_mean * v_dot_n_avg + f_1l = rho_ql_mean * v_dot_n_avg + f1 = f_1d + f_1v + f_1l + f2 = f1 * v1_avg + normal_direction[1] * p_int + f3 = f1 * v2_avg + normal_direction[2] * p_int + f4 = ((c_vd * inv_T_mean - K_avg) * f_1d + + (L_00 + c_vv * inv_T_mean - K_avg) * f_1v + + (c_pl * inv_T_mean - K_avg) * f_1l + v1_avg * f2 + v2_avg * f3) + + return SVector(f1, f2, f3, f4, f_1v, f_1l) + end + + varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", + "rho_v2", "rho_E", + "rho_qv", + "rho_ql") + varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", + "p", "qv", "ql") + varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", + "T", "qv", "ql") + varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "drypottemp", + "qv", "ql") + varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "moistpottemp", + "qv", "ql") + varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", + "T", "H", + "aeqpottemp") + varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "aeqpottemp", + "rv", "rt") end # @muladd - diff --git a/src/equations/equations.jl b/src/equations/equations.jl index 88da0e6..a1b3b53 100644 --- a/src/equations/equations.jl +++ b/src/equations/equations.jl @@ -1,5 +1,5 @@ using Trixi: AbstractEquations -abstract type AbstractCompressibleMoistEulerEquations{NDIMS, NVARS} <: AbstractEquations{NDIMS, NVARS} end +abstract type AbstractCompressibleMoistEulerEquations{NDIMS, NVARS} <: + AbstractEquations{NDIMS, NVARS} end include("compressible_moist_euler_2d.jl") - From 741b0b97bb1b1e10665d849f2d24899e8d05ea29 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 09:49:51 +0530 Subject: [PATCH 03/17] Fix typos --- examples/elixir_moist_euler_EC_bubble.jl | 42 ++++++++--------- examples/elixir_moist_euler_moist_bubble.jl | 48 ++++++++++---------- src/equations/compressible_moist_euler_2d.jl | 42 ++++++++--------- 3 files changed, 66 insertions(+), 66 deletions(-) diff --git a/examples/elixir_moist_euler_EC_bubble.jl b/examples/elixir_moist_euler_EC_bubble.jl index b8247d8..7efb686 100644 --- a/examples/elixir_moist_euler_EC_bubble.jl +++ b/examples/elixir_moist_euler_EC_bubble.jl @@ -21,7 +21,7 @@ function moist_state(y, dz, y0, r_t0, theta_e0, equations::CompressibleMoistEule 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[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)) * @@ -45,9 +45,9 @@ 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 + # structure: 1--> i-layer (z = total_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql LayerData::Matrix{RealT} # Contains the layer data for each height - total_hight::RealT # Total height of the atmosphere + total_height::RealT # Total height of the atmosphere 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 @@ -56,7 +56,7 @@ struct AtmossphereLayers{RealT<:Real} 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) +function AtmossphereLayers(equations ; total_height=10010.0, preciseness=10, ground_state=(1.4, 100000.0), equivalentpotential_temperature=320, mixing_ratios=(0.02, 0.02), RealT=Float64) @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 @@ -66,18 +66,18 @@ function AtmossphereLayers(equations ; total_hight=10010.0, preciseness=10, grou T0 = theta_e0 y0 = [p0, rho0, T0, r_t0, r_v0, rho_qv0, theta_e0] - n = convert(Int, total_hight / preciseness) + n = convert(Int, total_height / preciseness) 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) + 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) @@ -86,32 +86,32 @@ function AtmossphereLayers(equations ; total_hight=10010.0, preciseness=10, grou 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) + 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) + + return AtmossphereLayers{RealT}(equations, LayerData, total_height, dz, n, ground_state, theta_e0, mixing_ratios) 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 + @unpack LayerData, preciseness, total_height = AtmosphereLayers dz = preciseness - z = x[2] - if (z > total_hight && !(isapprox(z, total_hight))) + z = x[2] + if (z > total_height && !(isapprox(z, total_height))) 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) - z_r = z_l + dz + if (z_l == total_height) + z_r = z_l + dz n = n-1 end (rho_r, rho_theta_r, rho_qv_r, rho_ql_r) = LayerData[n+1, :] @@ -140,7 +140,7 @@ function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::Compr 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) @@ -150,14 +150,14 @@ function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::Compr # Assume pressure stays constant - if (r < rc && Δθ > 0) + 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 + 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 + if rt > 0 + while true T_loc = θ_loc * (p_loc / p_0)^kappa T_C = T_loc - 273.15 # SaturVapor diff --git a/examples/elixir_moist_euler_moist_bubble.jl b/examples/elixir_moist_euler_moist_bubble.jl index 0425281..281811e 100644 --- a/examples/elixir_moist_euler_moist_bubble.jl +++ b/examples/elixir_moist_euler_moist_bubble.jl @@ -21,7 +21,7 @@ function moist_state(y, dz, y0, r_t0, theta_e0, equations::CompressibleMoistEule 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[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)) * @@ -45,9 +45,9 @@ end struct AtmossphereLayers{RealT<:Real} equations::CompressibleMoistEulerEquations2D - # structure: 1--> i-layer (z = total_hight/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql + # structure: 1--> i-layer (z = total_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql LayerData::Matrix{RealT} - total_hight::RealT + total_height::RealT preciseness::Int layers::Int ground_state::NTuple{2, RealT} @@ -56,7 +56,7 @@ struct AtmossphereLayers{RealT<:Real} 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) +function AtmossphereLayers(equations ; total_height=10010.0, preciseness=10, ground_state=(1.4, 100000.0), equivalentpotential_temperature=320, mixing_ratios=(0.02, 0.02), RealT=Float64) @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 @@ -66,18 +66,18 @@ function AtmossphereLayers(equations ; total_hight=10010.0, preciseness=10, grou T0 = theta_e0 y0 = [p0, rho0, T0, r_t0, r_v0, rho_qv0, theta_e0] - n = convert(Int, total_hight / preciseness) + n = convert(Int, total_height / preciseness) 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) + 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) @@ -86,35 +86,35 @@ function AtmossphereLayers(equations ; total_hight=10010.0, preciseness=10, grou 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) + 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) + + return AtmossphereLayers{RealT}(equations, LayerData, total_height, dz, n, ground_state, theta_e0, mixing_ratios) end # Moist bubble test case from paper: # G.H. Bryan, J.M. Fritsch, A Benchmark Simulation for Moist Nonhydrostatic Numerical -# Models, MonthlyWeather Review Vol.130, 2917–2928, 2002, +# Models, MonthlyWeather Review Vol.130, 2917–2928, 2002, # https://journals.ametsoc.org/view/journals/mwre/130/12/1520-0493_2002_130_2917_absfmn_2.0.co_2.xml. function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, AtmosphereLayers::AtmossphereLayers) - @unpack LayerData, preciseness, total_hight = AtmosphereLayers + @unpack LayerData, preciseness, total_height = AtmosphereLayers dz = preciseness - z = x[2] - if (z > total_hight && !(isapprox(z, total_hight))) + z = x[2] + if (z > total_height && !(isapprox(z, total_height))) error("The atmossphere does not match the simulation domain") end n = convert(Int, floor((z+eps())/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) - z_r = z_l + dz + if (z_l == total_height) + z_r = z_l + dz n = n-1 end (rho_r, rho_theta_r, rho_qv_r, rho_ql_r) = LayerData[n+1, :] @@ -141,7 +141,7 @@ function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::Compr zc = 2000.0 rc = 2000.0 Δθ = 2.0 - + 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) @@ -158,7 +158,7 @@ function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::Compr r_l = rho_ql / rho_d r_t = r_v + r_l - # Aequivalentpotential temperature + # equivalentpotential temperature a=T_loc * (p_0 / p_d)^(R_d / (c_pd + r_t * c_pl)) b=H^(- r_v * R_v /c_pd) L_v = L_00 + (c_pv - c_pl) * T_loc @@ -166,18 +166,18 @@ function PerturbMoistProfile(x, rho, rho_theta, rho_qv, rho_ql, equations::Compr aeq_pot = (a * b *c) # Assume pressure stays constant - if (r < rc && Δθ > 0) + if (r < rc && Δθ > 0) # Calculate background density potential temperature θ_dens = rho_theta / rho * (p_loc / p_0)^(kappa_M - kappa) # Calculate perturbed density potential temperature θ_dens_new = θ_dens * (1 + Δθ * cospi(0.5*r/rc)^2 / 300) - rt =(rho_qv + rho_ql) / rho_d + rt =(rho_qv + rho_ql) / rho_d rv = rho_qv / rho_d # Calculate moist potential temperature θ_loc = θ_dens_new * (1 + rt)/(1 + (R_v / R_d) * rv) # Adjust varuables until the temperature is met - if rt > 0 - while true + if rt > 0 + while true T_loc = θ_loc * (p_loc / p_0)^kappa T_C = T_loc - 273.15 # SaturVapor @@ -246,7 +246,7 @@ coordinates_max = (20000.0, 10000.0) cells_per_dimension = (64, 32) # Create curved mesh with 64 x 32 elements -mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, periodicity = (false, false)) ############################################################################### diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl index a15a10e..70b347c 100644 --- a/src/equations/compressible_moist_euler_2d.jl +++ b/src/equations/compressible_moist_euler_2d.jl @@ -20,7 +20,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, R_v::RealT # moist air gas constant c_pl::RealT # liqid water constant g::RealT # gravitation constant - kappa::RealT # ratio of the gas constand R_d + kappa::RealT # ratio of the gas constant R_d gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications L_00::RealT # latent heat of evaporation at 0 K a::RealT @@ -91,7 +91,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, return SVector(f1, f2, f3, f4, f5, f6) end - # Slip-wall boundary condition + # Slip-wall boundary condition # Determine the boundary numerical surface flux for a slip wall condition. # Imposes a zero normal velocity at the wall. @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, @@ -115,7 +115,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, # Get the solution of the pressure Riemann problem # See Section 6.3.3 of # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Pratical Introduction + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) if v_normal <= 0.0 sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed @@ -139,7 +139,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, zero(eltype(u_inner))) * norm_ end - # Fix sign for structured mesh. + # Fix sign for structured mesh. @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, direction, x, t, surface_flux_function, @@ -314,7 +314,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - source_terms_moist_bubble(u_exact, x, t, equations)) - #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) + #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) # Note that d/dt rho = -d/dx rho = -d/dy rho. du1 += rho_x @@ -350,7 +350,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, zero(eltype(u)), zero(eltype(u))) end - # Raylight damping sponge source term form A. Sridhar et al., + # Raylight damping sponge source term form A. Sridhar et al., #Large-eddy simulations with ClimateMachine: a new open-sourcecode for #atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, #https://arxiv.org/abs/2110.00853 [physics.ao-ph] . @@ -423,14 +423,14 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, # saturation vapor pressure p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - # saturation density of vapor + # saturation density of vapor rho_star_qv = p_vs / (R_v * T) # Fisher-Burgmeister-Function a = rho_star_qv - rho_qv b = rho - rho_qv - rho_qd - # saturation control factor + # saturation control factor # < 1: stronger saturation effect # > 1: weaker saturation effect C = 1 @@ -447,10 +447,10 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, zero(eltype(u)), Q_ph, -Q_ph) end - # Low Mach number approximate Riemann solver (LMARS) from + # Low Mach number approximate Riemann solver (LMARS) from # X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. # Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian - # Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, + # Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, # https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. @inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector, equations::CompressibleMoistEulerEquations2D) @@ -468,7 +468,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - # diffusion parameter <= 1 + # diffusion parameter <= 1 beta = 1 # Compute the necessary interface flux components @@ -504,7 +504,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, return SVector(rho, v1, v2, p, qv, ql) end - # Convert conservative variables to primitive with + # Convert conservative variables to primitive with # temperature instead of pressure. @inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u @@ -570,7 +570,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) end - # Convert conservative variables to primitive with + # Convert conservative variables to primitive with # potential temperature instead of pressure. @inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u @@ -590,7 +590,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, return SVector(pot1, pot2, pot3, pot4, pot5, pot6) end - # Convert conservative variables to primitive with + # Convert conservative variables to primitive with # moist potential temperature instead of pressure. @inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u @@ -641,7 +641,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, pot3 = r_v + r_l pot4 = T pot5 = H - pot6 = aequivalent_pottemp_thermodynamic(u, equations) + pot6 = equivalent_pottemp_thermodynamic(u, equations) return SVector(pot1, pot2, pot3, pot4, pot5, pot6) end @@ -808,8 +808,8 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, return pot end - # Calculate the aequivalent potential temperature for a conservative state `cons`. - @inline function aequivalent_pottemp_thermodynamic(cons, + # Calculate the equivalent potential temperature for a conservative state `cons`. + @inline function equivalent_pottemp_thermodynamic(cons, equations::CompressibleMoistEulerEquations2D) @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons @@ -826,15 +826,15 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, L_v = L_00 + (c_pv - c_pl) * T c_p = c_pd + r_t * c_pl - #Aequivalentpotential temperature + #equivalentpotential temperature aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * exp(L_v * r_v * inv(c_p * T))) return aeq_pot end - # Convert conservative variables to primitive varuables with - # aequivalent potential temperature instead of pressure + # Convert conservative variables to primitive varuables with + # equivalent potential temperature instead of pressure # and mixing ratios innstead of specific contend. @inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations @@ -852,7 +852,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, L_v = L_00 + (c_pv - c_pl) * T c_p = c_pd + r_t * c_pl - #Aequivalentpotential temperature + #equivalentpotential temperature aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * exp(L_v * r_v * inv(c_p * T))) From 0716b9c356e2e95d64014d67e83197ddbe0aa97b Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 09:50:27 +0530 Subject: [PATCH 04/17] Format --- src/equations/compressible_moist_euler_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl index 70b347c..d36db3f 100644 --- a/src/equations/compressible_moist_euler_2d.jl +++ b/src/equations/compressible_moist_euler_2d.jl @@ -810,7 +810,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, # Calculate the equivalent potential temperature for a conservative state `cons`. @inline function equivalent_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) + equations::CompressibleMoistEulerEquations2D) @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons rho_d = rho - rho_qv - rho_ql From ca6eb523895d13f9d73f784e13475e0a56f63d49 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 11:17:14 +0530 Subject: [PATCH 05/17] Add 2 tests --- examples/elixir_moist_euler_EC_bubble.jl | 1 + examples/elixir_moist_euler_dry_bubble.jl | 12 ++++++------ test/Project.toml | 5 ++++- test/runtests.jl | 4 ++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/examples/elixir_moist_euler_EC_bubble.jl b/examples/elixir_moist_euler_EC_bubble.jl index 7efb686..e1657ba 100644 --- a/examples/elixir_moist_euler_EC_bubble.jl +++ b/examples/elixir_moist_euler_EC_bubble.jl @@ -9,6 +9,7 @@ using NLsolve: nlsolve equations = CompressibleMoistEulerEquations2D() +# TODO - Should the IC functions and struct be in the equation file? 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 diff --git a/examples/elixir_moist_euler_dry_bubble.jl b/examples/elixir_moist_euler_dry_bubble.jl index b456b63..7c10032 100644 --- a/examples/elixir_moist_euler_dry_bubble.jl +++ b/examples/elixir_moist_euler_dry_bubble.jl @@ -1,5 +1,5 @@ using OrdinaryDiffEq -using Trixi +using Trixi # TODO - Decide. This structure requires Trixi.jl to be in Project.toml of `Test` using TrixiAtmo using TrixiAtmo: flux_LMARS, source_terms_geopotential, cons2drypot @@ -29,10 +29,10 @@ function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEq θ = θ_ref + Δθ # potential temperature # π_exner = 1 - g / (c_pd * θ) * x[2] # exner pressure # rho = p_0 / (R_d * θ) * (π_exner)^(c_vd / R_d) # density - + # calculate background pressure with assumption hydrostatic and neutral p = p_0 * (1-kappa * g * x[2] / (R_d * θ_ref))^(c_pd / R_d) - + #calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π rho = p / ((p / p_0)^kappa*R_d*θ) T = p / (R_d * rho) @@ -42,7 +42,7 @@ function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEq v2 = 0.0 rho_v1 = rho * v1 rho_v2 = rho * v2 - rho_E = rho * c_vd * T + 1/2 * rho * (v1^2 + v2^2) + rho_E = rho * c_vd * T + 1/2 * rho * (v1^2 + v2^2) return SVector(rho, rho_v1, rho_v2, rho_E, zero(eltype(g)) ,zero(eltype(g))) end @@ -67,7 +67,7 @@ volume_flux = flux_chandrashekar volume_integral=VolumeIntegralFluxDifferencing(volume_flux) -# Create DG solver with polynomial degree = 4 and LMARS flux as surface flux +# Create DG solver with polynomial degree = 4 and LMARS flux as surface flux # and the EC flux (chandrashekar) as volume flux solver = DGSEM(basis, surface_flux, volume_integral) @@ -120,7 +120,7 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), maxiters=1.0e7, dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback save_everystep=false, callback=callbacks); diff --git a/test/Project.toml b/test/Project.toml index b3405d2..89a4f94 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,5 +1,8 @@ [deps] +NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Trixi = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb" [compat] -Test = "1" \ No newline at end of file +Test = "1" diff --git a/test/runtests.jl b/test/runtests.jl index a8e58d3..9ecd18a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,4 +21,8 @@ const TRIXI_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3) @test TrixiAtmo.baz() isa String end end + + @time if TRIXI_TEST == "all" || TRIXI_TEST == "moist_euler" + include("test_2d_moist_euler.jl") + end end From 92001fa7720b88cfdff456f448b82a023ffbdc86 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 11:23:11 +0530 Subject: [PATCH 06/17] Formatter --- examples/elixir_moist_euler_EC_bubble.jl | 355 ++++++++-------- examples/elixir_moist_euler_dry_bubble.jl | 107 +++-- examples/elixir_moist_euler_moist_bubble.jl | 392 +++++++++--------- ...oist_euler_nonhydrostatic_gravity_waves.jl | 121 +++--- .../elixir_moist_euler_source_terms_dry.jl | 28 +- .../elixir_moist_euler_source_terms_moist.jl | 29 +- ...ir_moist_euler_source_terms_split_moist.jl | 35 +- 7 files changed, 537 insertions(+), 530 deletions(-) diff --git a/examples/elixir_moist_euler_EC_bubble.jl b/examples/elixir_moist_euler_EC_bubble.jl index e1657ba..0c5b435 100644 --- a/examples/elixir_moist_euler_EC_bubble.jl +++ b/examples/elixir_moist_euler_EC_bubble.jl @@ -10,198 +10,208 @@ using NLsolve: nlsolve equations = CompressibleMoistEulerEquations2D() # TODO - Should the IC functions and struct be in the equation file? -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 +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 +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_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql - LayerData::Matrix{RealT} # Contains the layer data for each height - total_height::RealT # Total height of the atmosphere - 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. +struct AtmossphereLayers{RealT <: Real} + equations::CompressibleMoistEulerEquations2D + # structure: 1--> i-layer (z = total_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql + LayerData::Matrix{RealT} # Contains the layer data for each height + total_height::RealT # Total height of the atmosphere + 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_height = 10010.0, preciseness = 10, + ground_state = (1.4, 100000.0), + equivalentpotential_temperature = 320, + mixing_ratios = (0.02, 0.02), RealT = Float64) + @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 -function AtmossphereLayers(equations ; total_height=10010.0, preciseness=10, ground_state=(1.4, 100000.0), equivalentpotential_temperature=320, mixing_ratios=(0.02, 0.02), RealT=Float64) - @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_height / preciseness) - 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_qv0 = rho0 * r_v0 + T0 = theta_e0 + y0 = [p0, rho0, T0, r_t0, r_v0, rho_qv0, theta_e0] - 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) + n = convert(Int, total_height / preciseness) + dz = 0.01 + LayerData = zeros(RealT, n + 1, 4) - 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 + 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_height, dz, n, ground_state, theta_e0, mixing_ratios) + return AtmossphereLayers{RealT}(equations, LayerData, total_height, dz, n, ground_state, + theta_e0, mixing_ratios) 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_height = AtmosphereLayers - dz = preciseness - z = x[2] - if (z > total_height && !(isapprox(z, total_height))) - 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_height) - 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) +function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, + AtmosphereLayers::AtmossphereLayers) + @unpack LayerData, preciseness, total_height = AtmosphereLayers + dz = preciseness + z = x[2] + if (z > total_height && !(isapprox(z, total_height))) + 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_height) + 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) +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) - rho_theta = rho * θ_dens_new * (p_loc / p_0)^(kappa - kappa_M) + 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 - end - return SVector(rho, rho_e, rho_qv, rho_ql) + # 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) + 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) +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 @@ -211,16 +221,13 @@ source_term = source_terms_geopotential polydeg = 4 basis = LobattoLegendreBasis(polydeg) - surface_flux = flux_chandrashekar volume_flux = flux_chandrashekar - -volume_integral=VolumeIntegralFluxDifferencing(volume_flux) +volume_integral = VolumeIntegralFluxDifferencing(volume_flux) solver = DGSEM(basis, surface_flux, volume_integral) - coordinates_min = (0.0, 0.0) coordinates_max = (4000.0, 4000.0) @@ -233,8 +240,8 @@ 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) + boundary_conditions = boundary_condition, + source_terms = source_term) ############################################################################### # ODE solvers, callbacks etc. @@ -248,16 +255,17 @@ summary_callback = SummaryCallback() analysis_interval = 1000 solution_variables = cons2aeqpot -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, extra_analysis_errors=(:entropy_conservation_error, )) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_errors = (:entropy_conservation_error,)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=1000, - save_initial_solution=true, - save_final_solution=true, - solution_variables=solution_variables) +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = solution_variables) -stepsize_callback = StepsizeCallback(cfl=0.8) +stepsize_callback = StepsizeCallback(cfl = 0.8) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -268,9 +276,8 @@ callbacks = CallbackSet(summary_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); +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 diff --git a/examples/elixir_moist_euler_dry_bubble.jl b/examples/elixir_moist_euler_dry_bubble.jl index 7c10032..3d68bd4 100644 --- a/examples/elixir_moist_euler_dry_bubble.jl +++ b/examples/elixir_moist_euler_dry_bubble.jl @@ -13,45 +13,45 @@ equations = CompressibleMoistEulerEquations2D() # for the elastic equations incorporating second-order Runge–Kutta # time differencing. Mon. Wea. Rev., 126, 1992–1999. function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEquations2D) - @unpack p_0, kappa, g, c_pd, c_vd, R_d, R_v = equations - xc = 10000.0 - zc = 2000.0 - r = sqrt((x[1] - xc)^2 + (x[2] - zc)^2) - rc = 2000.0 - θ_ref = 300.0 - Δθ = 0.0 - - if r <= rc - Δθ = 2 * cospi(0.5*r/rc)^2 - end - - #Perturbed state: - θ = θ_ref + Δθ # potential temperature - # π_exner = 1 - g / (c_pd * θ) * x[2] # exner pressure - # rho = p_0 / (R_d * θ) * (π_exner)^(c_vd / R_d) # density - - # calculate background pressure with assumption hydrostatic and neutral - p = p_0 * (1-kappa * g * x[2] / (R_d * θ_ref))^(c_pd / R_d) - - #calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π - rho = p / ((p / p_0)^kappa*R_d*θ) - T = p / (R_d * rho) - - v1 = 20.0 - #v1 = 0.0 - v2 = 0.0 - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_E = rho * c_vd * T + 1/2 * rho * (v1^2 + v2^2) - return SVector(rho, rho_v1, rho_v2, rho_E, zero(eltype(g)) ,zero(eltype(g))) + @unpack p_0, kappa, g, c_pd, c_vd, R_d, R_v = equations + xc = 10000.0 + zc = 2000.0 + r = sqrt((x[1] - xc)^2 + (x[2] - zc)^2) + rc = 2000.0 + θ_ref = 300.0 + Δθ = 0.0 + + if r <= rc + Δθ = 2 * cospi(0.5 * r / rc)^2 + end + + #Perturbed state: + θ = θ_ref + Δθ # potential temperature + # π_exner = 1 - g / (c_pd * θ) * x[2] # exner pressure + # rho = p_0 / (R_d * θ) * (π_exner)^(c_vd / R_d) # density + + # calculate background pressure with assumption hydrostatic and neutral + p = p_0 * (1 - kappa * g * x[2] / (R_d * θ_ref))^(c_pd / R_d) + + #calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π + rho = p / ((p / p_0)^kappa * R_d * θ) + T = p / (R_d * rho) + + v1 = 20.0 + #v1 = 0.0 + v2 = 0.0 + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_E = rho * c_vd * T + 1 / 2 * rho * (v1^2 + v2^2) + return SVector(rho, rho_v1, rho_v2, rho_E, zero(eltype(g)), zero(eltype(g))) end initial_condition = initial_condition_warm_bubble -boundary_condition = (x_neg=boundary_condition_periodic, - x_pos=boundary_condition_periodic, - y_neg=boundary_condition_slip_wall, - y_pos=boundary_condition_slip_wall) +boundary_condition = (x_neg = boundary_condition_periodic, + x_pos = boundary_condition_periodic, + y_neg = boundary_condition_slip_wall, + y_pos = boundary_condition_slip_wall) # Gravity source since Q_ph=0 source_term = source_terms_geopotential @@ -64,8 +64,7 @@ basis = LobattoLegendreBasis(polydeg) surface_flux = flux_LMARS volume_flux = flux_chandrashekar -volume_integral=VolumeIntegralFluxDifferencing(volume_flux) - +volume_integral = VolumeIntegralFluxDifferencing(volume_flux) # Create DG solver with polynomial degree = 4 and LMARS flux as surface flux # and the EC flux (chandrashekar) as volume flux @@ -74,23 +73,22 @@ solver = DGSEM(basis, surface_flux, volume_integral) coordinates_min = (0.0, 0.0) coordinates_max = (20000.0, 10000.0) - cells_per_dimension = (64, 32) # Create curved mesh with 64 x 32 elements -mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, periodicity = (true, false)) +mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max, + periodicity = (true, false)) # A semidiscretization collects data structures and functions for the spatial discretization semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - boundary_conditions=boundary_condition, - source_terms=source_term) + boundary_conditions = boundary_condition, + source_terms = source_term) ############################################################################### # ODE solvers, callbacks etc. tspan = (0.0, 1000.0) - # Create ODE problem with time span from 0.0 to 1000.0 ode = semidiscretize(semi, tspan) @@ -99,17 +97,17 @@ summary_callback = SummaryCallback() analysis_interval = 1000 solution_variables = cons2drypot -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, extra_analysis_errors=(:entropy_conservation_error, )) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_errors = (:entropy_conservation_error,)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=1000, - save_initial_solution=true, - save_final_solution=true, - solution_variables=solution_variables) +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = solution_variables) - -stepsize_callback = StepsizeCallback(cfl=0.2) +stepsize_callback = StepsizeCallback(cfl = 0.2) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -117,11 +115,10 @@ callbacks = CallbackSet(summary_callback, save_solution, stepsize_callback) - ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - maxiters=1.0e7, - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + maxiters = 1.0e7, + 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 diff --git a/examples/elixir_moist_euler_moist_bubble.jl b/examples/elixir_moist_euler_moist_bubble.jl index 281811e..4a34160 100644 --- a/examples/elixir_moist_euler_moist_bubble.jl +++ b/examples/elixir_moist_euler_moist_bubble.jl @@ -9,203 +9,214 @@ using NLsolve: nlsolve 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 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] -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 + 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 -struct AtmossphereLayers{RealT<:Real} - equations::CompressibleMoistEulerEquations2D - # structure: 1--> i-layer (z = total_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql - LayerData::Matrix{RealT} - total_height::RealT - preciseness::Int - layers::Int - ground_state::NTuple{2, RealT} - equivalentpotential_temperature::RealT - mixing_ratios::NTuple{2, RealT} +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 +struct AtmossphereLayers{RealT <: Real} + equations::CompressibleMoistEulerEquations2D + # structure: 1--> i-layer (z = total_height/precision *(i-1)), 2--> rho, rho_theta, rho_qv, rho_ql + LayerData::Matrix{RealT} + total_height::RealT + preciseness::Int + layers::Int + ground_state::NTuple{2, RealT} + equivalentpotential_temperature::RealT + mixing_ratios::NTuple{2, RealT} +end -function AtmossphereLayers(equations ; total_height=10010.0, preciseness=10, ground_state=(1.4, 100000.0), equivalentpotential_temperature=320, mixing_ratios=(0.02, 0.02), RealT=Float64) - @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_height / preciseness) - dz = 0.01 - LayerData = zeros(RealT, n+1, 4) +function AtmossphereLayers(equations; total_height = 10010.0, preciseness = 10, + ground_state = (1.4, 100000.0), + equivalentpotential_temperature = 320, + mixing_ratios = (0.02, 0.02), RealT = Float64) + @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 - 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_qv0 = rho0 * r_v0 + T0 = theta_e0 + y0 = [p0, rho0, T0, r_t0, r_v0, rho_qv0, theta_e0] - 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) + n = convert(Int, total_height / preciseness) + dz = 0.01 + LayerData = zeros(RealT, n + 1, 4) - 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 + 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_height, dz, n, ground_state, theta_e0, mixing_ratios) + return AtmossphereLayers{RealT}(equations, LayerData, total_height, dz, n, ground_state, + theta_e0, mixing_ratios) end # Moist bubble test case from paper: # G.H. Bryan, J.M. Fritsch, A Benchmark Simulation for Moist Nonhydrostatic Numerical # Models, MonthlyWeather Review Vol.130, 2917–2928, 2002, # https://journals.ametsoc.org/view/journals/mwre/130/12/1520-0493_2002_130_2917_absfmn_2.0.co_2.xml. -function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, AtmosphereLayers::AtmossphereLayers) - @unpack LayerData, preciseness, total_height = AtmosphereLayers - dz = preciseness - z = x[2] - if (z > total_height && !(isapprox(z, total_height))) - error("The atmossphere does not match the simulation domain") - end - n = convert(Int, floor((z+eps())/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_height) - 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 = 0.0 - v2 = 0.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) +function initial_condition_moist_bubble(x, t, equations::CompressibleMoistEulerEquations2D, + AtmosphereLayers::AtmossphereLayers) + @unpack LayerData, preciseness, total_height = AtmosphereLayers + dz = preciseness + z = x[2] + if (z > total_height && !(isapprox(z, total_height))) + error("The atmossphere does not match the simulation domain") + end + n = convert(Int, floor((z + eps()) / 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_height) + 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 = 0.0 + v2 = 0.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 -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 = 10000.0 - zc = 2000.0 - rc = 2000.0 - Δθ = 2.0 - - 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 - - p_v = rho_qv * R_v * T_loc - p_d = p_loc - p_v - T_C = T_loc - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - - # equivalentpotential temperature - a=T_loc * (p_0 / p_d)^(R_d / (c_pd + r_t * c_pl)) - b=H^(- r_v * R_v /c_pd) - L_v = L_00 + (c_pv - c_pl) * T_loc - c=exp(L_v * r_v / ((c_pd + r_t * c_pl) * T_loc)) - aeq_pot = (a * b *c) - - # Assume pressure stays constant - if (r < rc && Δθ > 0) - # Calculate background density potential temperature - θ_dens = rho_theta / rho * (p_loc / p_0)^(kappa_M - kappa) - # Calculate perturbed density potential temperature - θ_dens_new = θ_dens * (1 + Δθ * cospi(0.5*r/rc)^2 / 300) - rt =(rho_qv + rho_ql) / rho_d - rv = rho_qv / rho_d - # Calculate moist potential temperature - θ_loc = θ_dens_new * (1 + rt)/(1 + (R_v / R_d) * rv) - # Adjust varuables until the temperature is met - 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) +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 = 10000.0 + zc = 2000.0 + rc = 2000.0 + Δθ = 2.0 + + 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) - rho_theta = rho * θ_dens_new * (p_loc / p_0)^(kappa - kappa_M) + 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 - end - return SVector(rho, rho_e, rho_qv, rho_ql) + + p_v = rho_qv * R_v * T_loc + p_d = p_loc - p_v + T_C = T_loc - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + + # equivalentpotential temperature + a = T_loc * (p_0 / p_d)^(R_d / (c_pd + r_t * c_pl)) + b = H^(-r_v * R_v / c_pd) + L_v = L_00 + (c_pv - c_pl) * T_loc + c = exp(L_v * r_v / ((c_pd + r_t * c_pl) * T_loc)) + aeq_pot = (a * b * c) + + # Assume pressure stays constant + if (r < rc && Δθ > 0) + # Calculate background density potential temperature + θ_dens = rho_theta / rho * (p_loc / p_0)^(kappa_M - kappa) + # Calculate perturbed density potential temperature + θ_dens_new = θ_dens * (1 + Δθ * cospi(0.5 * r / rc)^2 / 300) + rt = (rho_qv + rho_ql) / rho_d + rv = rho_qv / rho_d + # Calculate moist potential temperature + θ_loc = θ_dens_new * (1 + rt) / (1 + (R_v / R_d) * rv) + # Adjust varuables until the temperature is met + 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 # Create background atmosphere data set @@ -213,15 +224,15 @@ AtmossphereData = AtmossphereLayers(equations) # Create the initial condition with the initial data set function initial_condition_moist(x, t, equations) - return initial_condition_moist_bubble(x, t, equations, AtmossphereData) + return initial_condition_moist_bubble(x, t, equations, AtmossphereData) end initial_condition = initial_condition_moist -boundary_condition = (x_neg=boundary_condition_slip_wall, - x_pos=boundary_condition_slip_wall, - y_neg=boundary_condition_slip_wall, - y_pos=boundary_condition_slip_wall) +boundary_condition = (x_neg = boundary_condition_slip_wall, + x_pos = boundary_condition_slip_wall, + y_neg = boundary_condition_slip_wall, + y_pos = boundary_condition_slip_wall) source_term = source_terms_moist_bubble @@ -234,12 +245,10 @@ basis = LobattoLegendreBasis(polydeg) surface_flux = flux_LMARS volume_flux = flux_chandrashekar - -volume_integral=VolumeIntegralFluxDifferencing(volume_flux) +volume_integral = VolumeIntegralFluxDifferencing(volume_flux) solver = DGSEM(basis, surface_flux, volume_integral) - coordinates_min = (0.0, 0.0) coordinates_max = (20000.0, 10000.0) @@ -253,8 +262,8 @@ 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) + boundary_conditions = boundary_condition, + source_terms = source_term) ############################################################################### # ODE solvers, callbacks etc. @@ -267,16 +276,19 @@ summary_callback = SummaryCallback() analysis_interval = 1000 solution_variables = cons2aeqpot -analysis_callback = AnalysisCallback(semi, interval=analysis_interval, extra_analysis_errors=(:entropy_conservation_error, ), extra_analysis_integrals=(entropy, energy_total, saturation_pressure)) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval, + extra_analysis_errors = (:entropy_conservation_error,), + extra_analysis_integrals = (entropy, energy_total, + saturation_pressure)) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=1000, - save_initial_solution=true, - save_final_solution=true, - solution_variables=solution_variables) +save_solution = SaveSolutionCallback(interval = 1000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = solution_variables) -stepsize_callback = StepsizeCallback(cfl=0.2) +stepsize_callback = StepsizeCallback(cfl = 0.2) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -287,7 +299,7 @@ callbacks = CallbackSet(summary_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); +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 diff --git a/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl index 8d0fc12..b3b87c3 100644 --- a/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl +++ b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl @@ -1,7 +1,7 @@ using OrdinaryDiffEq using Trixi, TrixiAtmo using TrixiAtmo: CompressibleMoistEulerEquations2D, source_terms_geopotential, - source_terms_phase_change, + source_terms_phase_change, source_terms_nonhydrostatic_raylight_sponge, cons2drypot, flux_LMARS @@ -12,26 +12,27 @@ using TrixiAtmo: CompressibleMoistEulerEquations2D, source_terms_geopotential, # W. A. Gallus JR., J. B. Klemp, Behavior of Flow over Step Orography, Monthly Weather # Review Vol. 128.4, pages 1153–1164, 2000, # https://doi.org/10.1175/1520-0493(2000)128<1153:BOFOSO>2.0.CO;2. -function initial_condition_nonhydrostatic_gravity_wave(x, t, equations::CompressibleMoistEulerEquations2D) - @unpack p_0, kappa, gamma, g, c_pd, c_vd, R_d, R_v = equations - z = x[2] - T_0 = 280.0 - theta_0 = T_0 - N = 0.01 - - theta = theta_0 * exp(N^2 *z / g) - p = p_0*(1 + g^2 * inv(c_pd * theta_0 * N^2) * (exp(-z * N^2 / g) - 1))^(1/kappa) - rho = p / ((p / p_0)^kappa*R_d*theta) - T = p / (R_d * rho) - - v1 = 10 - v2 = 0 - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_E = rho * c_vd * T + 0.5 * rho * (v1^2 + v2^2) - rho_qv = 0 - rho_ql = 0 - return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) +function initial_condition_nonhydrostatic_gravity_wave(x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack p_0, kappa, gamma, g, c_pd, c_vd, R_d, R_v = equations + z = x[2] + T_0 = 280.0 + theta_0 = T_0 + N = 0.01 + + theta = theta_0 * exp(N^2 * z / g) + p = p_0 * (1 + g^2 * inv(c_pd * theta_0 * N^2) * (exp(-z * N^2 / g) - 1))^(1 / kappa) + rho = p / ((p / p_0)^kappa * R_d * theta) + T = p / (R_d * rho) + + v1 = 10 + v2 = 0 + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_E = rho * c_vd * T + 0.5 * rho * (v1^2 + v2^2) + rho_qv = 0 + rho_ql = 0 + return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) end equations = CompressibleMoistEulerEquations2D() @@ -39,60 +40,59 @@ equations = CompressibleMoistEulerEquations2D() initial_condition = initial_condition_nonhydrostatic_gravity_wave function source(u, x, t, equations::CompressibleMoistEulerEquations2D) - return (source_terms_geopotential(u, equations) + - source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) + - source_terms_nonhydrostatic_raylight_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D)) + return (source_terms_geopotential(u, equations) + + source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) + + source_terms_nonhydrostatic_raylight_sponge(u, x, t, + equations::CompressibleMoistEulerEquations2D)) end -source_term=source +source_term = source -boundary_conditions = ( - x_neg=boundary_condition_periodic, - x_pos=boundary_condition_periodic, - y_neg=boundary_condition_slip_wall, - y_pos=boundary_condition_slip_wall, - ) +boundary_conditions = (x_neg = boundary_condition_periodic, + x_pos = boundary_condition_periodic, + y_neg = boundary_condition_slip_wall, + y_pos = boundary_condition_slip_wall) polydeg = 4 basis = LobattoLegendreBasis(polydeg) surface_flux = flux_LMARS volume_flux = flux_chandrashekar - -volume_integral=VolumeIntegralFluxDifferencing(volume_flux) - -solver = DGSEM(basis, surface_flux, volume_integral) +volume_integral = VolumeIntegralFluxDifferencing(volume_flux) +solver = DGSEM(basis, surface_flux, volume_integral) # Deformed rectangle that has "witch of agnesi" as bottom function bottom(x) - h = 400.0 - a = 1000.0 - x_length = 40000.0 - # linear function cx for x in [-1,1] - c = x_length / 2 - # return (cx , f(cx)-f(c)) - return SVector(c * x , (h * a^2 * inv((c * x)^2+a^2)) - (h * a^2 * inv((c)^2+a^2))) + h = 400.0 + a = 1000.0 + x_length = 40000.0 + # linear function cx for x in [-1,1] + c = x_length / 2 + # return (cx , f(cx)-f(c)) + return SVector(c * x, (h * a^2 * inv((c * x)^2 + a^2)) - (h * a^2 * inv((c)^2 + a^2))) end f1(s) = SVector(-20000.0, 8000.0 * s + 8000.0) -f2(s) = SVector( 20000.0, 8000.0 * s + 8000.0) -f3(s) = SVector( 20000.0 * s , (400.0 * 1000.0^2 * inv((20000.0 * s)^2+1000.0^2))-(400.0 * 1000.0^2 * inv((20000.0)^2+1000.0^2))) -f4(s) = SVector( 20000.0 * s, 16000.0) - +f2(s) = SVector(20000.0, 8000.0 * s + 8000.0) +function f3(s) + SVector(20000.0 * s, + (400.0 * 1000.0^2 * inv((20000.0 * s)^2 + 1000.0^2)) - + (400.0 * 1000.0^2 * inv((20000.0)^2 + 1000.0^2))) +end +f4(s) = SVector(20000.0 * s, 16000.0) faces = (f1, f2, f3, f4) - # dx = 0.2*a dz = 10-200 m für (40,16) km cells_per_dimension = (100, 80) - mesh = StructuredMesh(cells_per_dimension, faces, periodicity = (true, false)) semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_term, boundary_conditions=boundary_conditions) + source_terms = source_term, + boundary_conditions = boundary_conditions) ############################################################################### # ODE solvers, callbacks etc. @@ -105,17 +105,16 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 10000 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) - -alive_callback = AliveCallback(analysis_interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=5000, - save_initial_solution=true, - save_final_solution=true, - solution_variables=cons2drypot) +alive_callback = AliveCallback(analysis_interval = analysis_interval) +save_solution = SaveSolutionCallback(interval = 5000, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2drypot) -stepsize_callback = StepsizeCallback(cfl=0.2) +stepsize_callback = StepsizeCallback(cfl = 0.2) callbacks = CallbackSet(summary_callback, analysis_callback, @@ -123,14 +122,12 @@ callbacks = CallbackSet(summary_callback, save_solution, stepsize_callback); - ############################################################################### # run the simulation - sol = solve(ode, SSPRK33(), - maxiters=1.0e7, - dt=1, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); + maxiters = 1.0e7, + dt = 1, # 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 diff --git a/examples/elixir_moist_euler_source_terms_dry.jl b/examples/elixir_moist_euler_source_terms_dry.jl index 9d88ca8..efe7a52 100644 --- a/examples/elixir_moist_euler_source_terms_dry.jl +++ b/examples/elixir_moist_euler_source_terms_dry.jl @@ -8,11 +8,11 @@ using TrixiAtmo: source_terms_convergence_test_dry, initial_condition_convergenc ############################################################################### # semidiscretization of the compressible Euler equations -equations = CompressibleMoistEulerEquations2D( ;g=0.0) +equations = CompressibleMoistEulerEquations2D(; g = 0.0) initial_condition = initial_condition_convergence_test_dry -solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs) +solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs) coordinates_min = (0.0, 0.0) coordinates_max = (2.0, 2.0) @@ -21,10 +21,8 @@ cells_per_dimension = (16, 16) mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) - semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test_dry) - + source_terms = source_terms_convergence_test_dry) ############################################################################### # ODE solvers, callbacks etc. @@ -35,16 +33,16 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=100, - save_initial_solution=true, - save_final_solution=true, - solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) -stepsize_callback = StepsizeCallback(cfl=1.0) +stepsize_callback = StepsizeCallback(cfl = 1.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -54,7 +52,7 @@ callbacks = CallbackSet(summary_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); +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 diff --git a/examples/elixir_moist_euler_source_terms_moist.jl b/examples/elixir_moist_euler_source_terms_moist.jl index 08d9e39..f5550d7 100644 --- a/examples/elixir_moist_euler_source_terms_moist.jl +++ b/examples/elixir_moist_euler_source_terms_moist.jl @@ -3,12 +3,13 @@ using OrdinaryDiffEq using Trixi, TrixiAtmo -using TrixiAtmo: source_terms_convergence_test_moist, initial_condition_convergence_test_moist +using TrixiAtmo: source_terms_convergence_test_moist, + initial_condition_convergence_test_moist ############################################################################### # semidiscretization of the compressible Euler equations -equations = CompressibleMoistEulerEquations2D() +equations = CompressibleMoistEulerEquations2D() initial_condition = initial_condition_convergence_test_moist @@ -26,10 +27,8 @@ cells_per_dimension = (4, 4) mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) - semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test_moist) - + source_terms = source_terms_convergence_test_moist) ############################################################################### # ODE solvers, callbacks etc. @@ -40,16 +39,16 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=100, - save_initial_solution=true, - save_final_solution=true, - solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) -stepsize_callback = StepsizeCallback(cfl=1.0) +stepsize_callback = StepsizeCallback(cfl = 1.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -59,7 +58,7 @@ callbacks = CallbackSet(summary_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); +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 diff --git a/examples/elixir_moist_euler_source_terms_split_moist.jl b/examples/elixir_moist_euler_source_terms_split_moist.jl index 73bb0ae..f0e2415 100644 --- a/examples/elixir_moist_euler_source_terms_split_moist.jl +++ b/examples/elixir_moist_euler_source_terms_split_moist.jl @@ -3,24 +3,23 @@ using OrdinaryDiffEq using Trixi, TrixiAtmo -using TrixiAtmo: source_terms_convergence_test_moist, initial_condition_convergence_test_moist +using TrixiAtmo: source_terms_convergence_test_moist, + initial_condition_convergence_test_moist ############################################################################### # semidiscretization of the compressible Euler equations -equations = CompressibleMoistEulerEquations2D() +equations = CompressibleMoistEulerEquations2D() initial_condition = initial_condition_convergence_test_moist polydeg = 4 basis = LobattoLegendreBasis(polydeg) - surface_flux = flux_chandrashekar volume_flux = flux_chandrashekar - -volume_integral=VolumeIntegralFluxDifferencing(volume_flux) +volume_integral = VolumeIntegralFluxDifferencing(volume_flux) solver = DGSEM(basis, surface_flux, volume_integral) @@ -31,10 +30,8 @@ cells_per_dimension = (4, 4) mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max) - semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, - source_terms=source_terms_convergence_test_moist) - + source_terms = source_terms_convergence_test_moist) ############################################################################### # ODE solvers, callbacks etc. @@ -45,16 +42,16 @@ ode = semidiscretize(semi, tspan) summary_callback = SummaryCallback() analysis_interval = 100 -analysis_callback = AnalysisCallback(semi, interval=analysis_interval) +analysis_callback = AnalysisCallback(semi, interval = analysis_interval) -alive_callback = AliveCallback(analysis_interval=analysis_interval) +alive_callback = AliveCallback(analysis_interval = analysis_interval) -save_solution = SaveSolutionCallback(interval=100, - save_initial_solution=true, - save_final_solution=true, - solution_variables=cons2prim) +save_solution = SaveSolutionCallback(interval = 100, + save_initial_solution = true, + save_final_solution = true, + solution_variables = cons2prim) -stepsize_callback = StepsizeCallback(cfl=1.0) +stepsize_callback = StepsizeCallback(cfl = 1.0) callbacks = CallbackSet(summary_callback, analysis_callback, alive_callback, @@ -64,8 +61,8 @@ callbacks = CallbackSet(summary_callback, ############################################################################### # run the simulation -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - maxiters=1.0e7, - dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback - save_everystep=false, callback=callbacks); +sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false), + maxiters = 1.0e7, + 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 From 1aebf65f3b3959a1fa2d1aab72908fa889aa7709 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 17 Jul 2024 11:56:24 +0530 Subject: [PATCH 07/17] Add missing files --- test/test_2d_moist_euler.jl | 75 +++++++++++ test/test_trixiatmo.jl | 243 ++++++++++++++++++++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 test/test_2d_moist_euler.jl create mode 100644 test/test_trixiatmo.jl diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl new file mode 100644 index 0000000..87a1c5e --- /dev/null +++ b/test/test_2d_moist_euler.jl @@ -0,0 +1,75 @@ +module TestExamples2DMoistEuler + +using Test +using TrixiAtmo + +include("test_trixiatmo.jl") # TODO - This is a repetition from Trixi.jl + +EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory for examples? + +@trixi_testset "elixir_moist_euler_dry_bubble.jl" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_dry_bubble.jl"), + l2=[ + 1.300428671901329e-6, + 2.601090012108739e-5, + 0.0006660124630171347, + 0.008969786054960861, + 0.0, + 0.0, + ], + linf=[ + 1.0312042909910168e-5, + 0.00020625488871672815, + 0.006392107590872236, + 0.07612038028310053, + 0.0, + 0.0, + ], + polydeg=3, + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + analysis_callback(sol) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 2000 + # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. + end +end + +@trixi_testset "elixir_moist_euler_EC_bubble" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_EC_bubble.jl"), + l2=[ + 0.01345154393018332, + 0.8070926361417218, + 7.938812668709457, + 4500.747616411578, + 0.00015592413050814787, + 0.00014163475049532796, + ], + linf=[ + 0.1427479052298466, + 8.564879578662357, + 91.56822550162855, + 49528.383866247605, + 0.0019364397602254623, + 0.0013259689889851285, + ], + polydeg=3, + cells_per_dimension=(16, 16), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + analysis_callback(sol) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 2000 + # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. + end +end + +end # module diff --git a/test/test_trixiatmo.jl b/test/test_trixiatmo.jl new file mode 100644 index 0000000..bb600a2 --- /dev/null +++ b/test/test_trixiatmo.jl @@ -0,0 +1,243 @@ +using Test: @test +import TrixiAtmo + +# Use a macro to avoid world age issues when defining new initial conditions etc. +# inside an elixir. +""" + @test_trixi_include(elixir; l2=nothing, linf=nothing, + atol=500*eps(), rtol=sqrt(eps()), + parameters...) + +Test Trixi by calling `trixi_include(elixir; parameters...)`. +By default, only the absence of error output is checked. +If `l2` or `linf` are specified, in addition the resulting L2/Linf errors +are compared approximately against these reference values, using `atol, rtol` +as absolute/relative tolerance. +""" +macro test_trixi_include(elixir, args...) + local l2 = get_kwarg(args, :l2, nothing) + local linf = get_kwarg(args, :linf, nothing) + local atol = get_kwarg(args, :atol, 500 * eps()) + local rtol = get_kwarg(args, :rtol, sqrt(eps())) + local skip_coverage = get_kwarg(args, :skip_coverage, false) + local coverage_override = expr_to_named_tuple(get_kwarg(args, :coverage_override, :())) + if !(:maxiters in keys(coverage_override)) + # maxiters in coverage_override defaults to 1 + coverage_override = (; coverage_override..., maxiters = 1) + end + + local cmd = string(Base.julia_cmd()) + local coverage = occursin("--code-coverage", cmd) && + !occursin("--code-coverage=none", cmd) + + local kwargs = Pair{Symbol, Any}[] + for arg in args + if (arg.head == :(=) && + !(arg.args[1] in (:l2, :linf, :atol, :rtol, :coverage_override, :skip_coverage)) + && !(coverage && arg.args[1] in keys(coverage_override))) + push!(kwargs, Pair(arg.args...)) + end + end + + if coverage + for key in keys(coverage_override) + push!(kwargs, Pair(key, coverage_override[key])) + end + end + + if coverage && skip_coverage + return quote + if TrixiAtmo.Trixi.mpi_isroot() + println("═"^100) + println("Skipping coverage test of ", $elixir) + println("═"^100) + println("\n\n") + end + end + end + + quote + TrixiAtmo.Trixi.mpi_isroot() && println("═"^100) + TrixiAtmo.Trixi.mpi_isroot() && println($elixir) + + # if `maxiters` is set in tests, it is usually set to a small number to + # run only a few steps - ignore possible warnings coming from that + if any(==(:maxiters) ∘ first, $kwargs) + additional_ignore_content = [ + r"┌ Warning: Interrupted\. Larger maxiters is needed\..*\n└ @ SciMLBase .+\n", + r"┌ Warning: Interrupted\. Larger maxiters is needed\..*\n└ @ Trixi .+\n"] + else + additional_ignore_content = [] + end + + # evaluate examples in the scope of the module they're called from + @test_nowarn_mod TrixiAtmo.Trixi.trixi_include(@__MODULE__, $elixir; $kwargs...) additional_ignore_content + + # if present, compare l2 and linf errors against reference values + if !$coverage && (!isnothing($l2) || !isnothing($linf)) + l2_measured, linf_measured = analysis_callback(sol) + + if TrixiAtmo.Trixi.mpi_isroot() && !isnothing($l2) + @test length($l2) == length(l2_measured) + for (l2_expected, l2_actual) in zip($l2, l2_measured) + @test isapprox(l2_expected, l2_actual, atol = $atol, rtol = $rtol) + end + end + + if TrixiAtmo.Trixi.mpi_isroot() && !isnothing($linf) + @test length($linf) == length(linf_measured) + for (linf_expected, linf_actual) in zip($linf, linf_measured) + @test isapprox(linf_expected, linf_actual, atol = $atol, rtol = $rtol) + end + end + end + + TrixiAtmo.Trixi.mpi_isroot() && println("═"^100) + TrixiAtmo.Trixi.mpi_isroot() && println("\n\n") + end +end + +# Get the first value assigned to `keyword` in `args` and return `default_value` +# if there are no assignments to `keyword` in `args`. +function get_kwarg(args, keyword, default_value) + val = default_value + for arg in args + if arg.head == :(=) && arg.args[1] == keyword + val = arg.args[2] + break + end + end + return val +end + +function expr_to_named_tuple(expr) + result = (;) + + for arg in expr.args + if arg.head != :(=) + error("Invalid expression") + end + result = (; result..., arg.args[1] => arg.args[2]) + end + return result +end + +# Modified version of `@test_nowarn` that prints the content of `stderr` when +# it is not empty and ignores module replacements. +macro test_nowarn_mod(expr, additional_ignore_content = String[]) + quote + let fname = tempname() + try + ret = open(fname, "w") do f + redirect_stderr(f) do + $(esc(expr)) + end + end + stderr_content = read(fname, String) + if !isempty(stderr_content) + println("Content of `stderr`:\n", stderr_content) + end + + # Patterns matching the following ones will be ignored. Additional patterns + # passed as arguments can also be regular expressions, so we just use the + # type `Any` for `ignore_content`. + ignore_content = Any[ + # We need to ignore steady state information reported by our callbacks + r"┌ Info: Steady state tolerance reached\n│ steady_state_callback .+\n└ t = .+\n", + # We also ignore our own compilation messages + "[ Info: You just called `trixi_include`. Julia may now compile the code, please be patient.\n", + # TODO: Upstream (PlotUtils). This should be removed again once the + # deprecated stuff is fixed upstream. + "WARNING: importing deprecated binding Colors.RGB1 into Plots.\n", + "WARNING: importing deprecated binding Colors.RGB4 into Plots.\n", + r"┌ Warning: Keyword argument letter not supported with Plots.+\n└ @ Plots.+\n", + r"┌ Warning: `parse\(::Type, ::Coloarant\)` is deprecated.+\n│.+\n│.+\n└ @ Plots.+\n", + # TODO: Silence warning introduced by Flux v0.13.13. Should be properly fixed. + r"┌ Warning: Layer with Float32 parameters got Float64 input.+\n│.+\n│.+\n│.+\n└ @ Flux.+\n", + # NOTE: These warnings arose from Julia 1.10 onwards + r"WARNING: Method definition .* in module .* at .* overwritten .*.\n", + # Warnings from third party packages + r"┌ Warning: Problem status ALMOST_INFEASIBLE; solution may be inaccurate.\n└ @ Convex ~/.julia/packages/Convex/.*\n", + r"┌ Warning: Problem status ALMOST_OPTIMAL; solution may be inaccurate.\n└ @ Convex ~/.julia/packages/Convex/.*\n"] + append!(ignore_content, $additional_ignore_content) + for pattern in ignore_content + stderr_content = replace(stderr_content, pattern => "") + end + + # We also ignore simple module redefinitions for convenience. Thus, we + # check whether every line of `stderr_content` is of the form of a + # module replacement warning. + @test occursin(r"^(WARNING: replacing module .+\.\n)*$", stderr_content) + ret + finally + rm(fname, force = true) + end + end + end +end + +""" + @timed_testset "name of the testset" #= code to test #= + +Similar to `@testset`, but prints the name of the testset and its runtime +after execution. +""" +macro timed_testset(name, expr) + @assert name isa String + quote + local time_start = time_ns() + @testset $name $expr + local time_stop = time_ns() + if TrixiAtmo.Trixi.mpi_isroot() + flush(stdout) + @info("Testset "*$name*" finished in " + *string(1.0e-9 * (time_stop - time_start))*" seconds.\n") + flush(stdout) + end + end +end + +""" + @trixi_testset "name of the testset" #= code to test #= + +Similar to `@testset`, but wraps the code inside a temporary module to avoid +namespace pollution. It also `include`s this file again to provide the +definition of `@test_trixi_include`. Moreover, it records the execution time +of the testset similarly to [`timed_testset`](@ref). +""" +macro trixi_testset(name, expr) + @assert name isa String + # TODO: `@eval` is evil + # We would like to use + # mod = gensym(name) + # ... + # module $mod + # to create new module names for every test set. However, this is not + # compatible with the dirty hack using `@eval` to get the mapping when + # loading structured, curvilinear meshes. Thus, we need to use a plain + # module name here. + quote + local time_start = time_ns() + @eval module TrixiTestModule + using Test + using TrixiAtmo + include(@__FILE__) + # We define `EXAMPLES_DIR` in (nearly) all test modules and use it to + # get the path to the elixirs to be tested. However, that's not required + # and we want to fail gracefully if it's not defined. + try + import ..EXAMPLES_DIR + catch + nothing + end + @testset $name $expr + end + local time_stop = time_ns() + if TrixiAtmo.Trixi.mpi_isroot() + flush(stdout) + @info("Testset "*$name*" finished in " + *string(1.0e-9 * (time_stop - time_start))*" seconds.\n") + end + nothing + end +end From 84baaf5cff4fa59011f4b645e1dcc2c428abd92f Mon Sep 17 00:00:00 2001 From: Arpit Babbar Date: Wed, 17 Jul 2024 13:16:41 +0530 Subject: [PATCH 08/17] Apply suggestions from code review (Minor typographical errors) --- examples/elixir_moist_euler_dry_bubble.jl | 6 +++--- src/equations/compressible_moist_euler_2d.jl | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/elixir_moist_euler_dry_bubble.jl b/examples/elixir_moist_euler_dry_bubble.jl index 3d68bd4..ab522a0 100644 --- a/examples/elixir_moist_euler_dry_bubble.jl +++ b/examples/elixir_moist_euler_dry_bubble.jl @@ -25,7 +25,7 @@ function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEq Δθ = 2 * cospi(0.5 * r / rc)^2 end - #Perturbed state: + # Perturbed state: θ = θ_ref + Δθ # potential temperature # π_exner = 1 - g / (c_pd * θ) * x[2] # exner pressure # rho = p_0 / (R_d * θ) * (π_exner)^(c_vd / R_d) # density @@ -33,12 +33,12 @@ function initial_condition_warm_bubble(x, t, equations::CompressibleMoistEulerEq # calculate background pressure with assumption hydrostatic and neutral p = p_0 * (1 - kappa * g * x[2] / (R_d * θ_ref))^(c_pd / R_d) - #calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π + # calculate rho and T with p and theta (now perturbed) rho = p / R_d T, T = θ / π rho = p / ((p / p_0)^kappa * R_d * θ) T = p / (R_d * rho) v1 = 20.0 - #v1 = 0.0 + # v1 = 0.0 v2 = 0.0 rho_v1 = rho * v1 rho_v2 = rho * v2 diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl index d36db3f..82d7c1a 100644 --- a/src/equations/compressible_moist_euler_2d.jl +++ b/src/equations/compressible_moist_euler_2d.jl @@ -309,12 +309,12 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, dE = E * rho_x + rho * mu * dT dp = xi * (T * rho_x + rho * dT) - #Calculate Error in Sources with exact solution and u + # Calculate Error in Sources with exact solution and u u_exact = SVector(rho, rho, rho, rho * E, rho * qv, rho * ql) du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - source_terms_moist_bubble(u_exact, x, t, equations)) - #du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) + # du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) # Note that d/dt rho = -d/dx rho = -d/dy rho. du1 += rho_x @@ -351,9 +351,9 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, end # Raylight damping sponge source term form A. Sridhar et al., - #Large-eddy simulations with ClimateMachine: a new open-sourcecode for - #atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, - #https://arxiv.org/abs/2110.00853 [physics.ao-ph] . + # Large-eddy simulations with ClimateMachine: a new open-sourcecode for + # atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, + # https://arxiv.org/abs/2110.00853 [physics.ao-ph] . @inline function source_terms_nonhydrostatic_raylight_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D) rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u @@ -369,7 +369,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, z_top = 16000.0 # positive even power with default value 2 gamma = 2.0 - #relaxation coefficient > 0 + # relaxation coefficient > 0 alpha = 0.5 tau_s = zero(eltype(u)) @@ -528,7 +528,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, v_square = v1^2 + v2^2 rho_qd = rho - rho_qv - rho_ql - # Work around if an individual density is zero + # Workaround if an individual density is zero # Thermodynamic entropy s_d = 0 s_v = 0 @@ -826,7 +826,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, L_v = L_00 + (c_pv - c_pl) * T c_p = c_pd + r_t * c_pl - #equivalentpotential temperature + # equivalent potential temperature aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * exp(L_v * r_v * inv(c_p * T))) @@ -852,7 +852,7 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, L_v = L_00 + (c_pv - c_pl) * T c_p = c_pd + r_t * c_pl - #equivalentpotential temperature + # equivalent potential temperature aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * exp(L_v * r_v * inv(c_p * T))) From 63d9606520b3448f4ffe7e01fb41a0871f30f232 Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Wed, 24 Jul 2024 08:57:58 +0200 Subject: [PATCH 09/17] renamed to compressible_moist_euler_2d_lucas.jl --- src/equations/compressible_moist_euler_2d.jl | 1029 ------------------ 1 file changed, 1029 deletions(-) delete mode 100644 src/equations/compressible_moist_euler_2d.jl diff --git a/src/equations/compressible_moist_euler_2d.jl b/src/equations/compressible_moist_euler_2d.jl deleted file mode 100644 index 82d7c1a..0000000 --- a/src/equations/compressible_moist_euler_2d.jl +++ /dev/null @@ -1,1029 +0,0 @@ -using Trixi -using Trixi: ln_mean, inv_ln_mean -import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, - cons2prim, cons2entropy, max_abs_speed_naive, max_abs_speeds, - entropy, energy_total, flux - -# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). -# Since these FMAs can increase the performance of many numerical algorithms, -# we need to opt-in explicitly. -# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. -@muladd begin - struct CompressibleMoistEulerEquations2D{RealT <: Real} <: - AbstractCompressibleMoistEulerEquations{2, 6} - p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) - c_pd::RealT # dry air constant - c_vd::RealT # dry air constant - R_d::RealT # dry air gas constant - c_pv::RealT # moist air constant - c_vv::RealT # moist air constant - R_v::RealT # moist air gas constant - c_pl::RealT # liqid water constant - g::RealT # gravitation constant - kappa::RealT # ratio of the gas constant R_d - gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications - L_00::RealT # latent heat of evaporation at 0 K - a::RealT - end - - function CompressibleMoistEulerEquations2D(; g = 9.81, RealT = Float64) - p_0 = 100000.0 - c_pd = 1004.0 - c_vd = 717.0 - R_d = c_pd - c_vd - c_pv = 1885.0 - c_vv = 1424.0 - R_v = c_pv - c_vv - c_pl = 4186.0 - gamma = c_pd / c_vd # = 1/(1 - kappa) - kappa = 1 - inv(gamma) - L_00 = 3147620.0 - a = 360.0 - return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, - R_v, c_pl, g, kappa, gamma, L_00, a) - end - - # Calculate 1D flux for a single point. - @inline function flux(u, orientation::Integer, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - f4 = (rho_E + p) * v1 - f5 = rho_v1 * qv - f6 = rho_v1 * ql - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - f4 = (rho_E + p) * v2 - f5 = rho_v2 * qv - f6 = rho_v2 * ql - end - return SVector(f1, f2, f3, f4, f5, f6) - end - - # Calculate 1D flux for a single point in the normal direction. - # Note, this directional vector is not normalized. - @inline function flux(u, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = (rho_v_normal) * v1 + p * normal_direction[1] - f3 = (rho_v_normal) * v2 + p * normal_direction[2] - f4 = (rho_e + p) * v_normal - f5 = rho_v_normal * qv - f6 = (rho_v_normal) * ql - return SVector(f1, f2, f3, f4, f5, f6) - end - - # Slip-wall boundary condition - # Determine the boundary numerical surface flux for a slip wall condition. - # Imposes a zero normal velocity at the wall. - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - x, t, - surface_flux_function, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - - # compute the primitive variables - rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, - equations) - qd_local = 1 - qv_local - ql_local - gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl) * - inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed - p_star = p_local * - (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * - inv(gamma - 1)) - else # v_normal > 0.0 - A = 2.0 / ((gamma + 1) * rho_local) - B = p_local * (gamma - 1) / (gamma + 1) - p_star = p_local + - 0.5 * v_normal / A * - (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) - end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2], - zero(eltype(u_inner)), - zero(eltype(u_inner)), - zero(eltype(u_inner))) * norm_ - end - - # Fix sign for structured mesh. - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - direction, x, t, - surface_flux_function, - equations::CompressibleMoistEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, - equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, - equations) - end - - return boundary_flux - end - - # Rotate momentum flux. The same as in compressible Euler. - @inline function rotate_to_x(u, normal_vector::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 n_2 0; - # 0 t_1 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3], - u[4], u[5], u[6]) - end - - # Recreates the convergence test initial condition from compressible euler 2D. - function initial_condition_convergence_test_dry(x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 0 - ql = 0 - - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - - T = (ini - 1) / c_vd - E = (mu * T + qv * L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) - end - - # Recreates the convergence test source term from compressible euler 2D. - @inline function source_terms_convergence_test_dry(u, x, t, - equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - qv = 0 - ql = 0 - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - T = (rho - 1) / c_vd - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) - - du1 += rho_x - du2 += rho_x + dp - du3 += du2 - du4 += dE + 2 * dp - du5 += qv * du1 - du6 += ql * du1 - - return SVector(du1, du2, du3, du4, du5, du6) - end - - # Initial condition for the convergence analysis. - # Extends the convergence test to the system with gravity and phase change. - function initial_condition_convergence_test_moist(x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 100 / L_00 - ql = qv / 10 - - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - - T = (ini - 1) / mu + 10 / c_vd + 40 - E = (mu * T + qv * L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) - end - - # Source term for the convergence analysis. - # Extends the convergence test to the system with gravity and phase change. - @inline function source_terms_convergence_test_moist(u, x, t, - equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - qv = 100 / L_00 - ql = qv / 10 - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - T = (rho - 1) / mu + 10 / c_vd + 40 - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - - # Calculate Error in Sources with exact solution and u - u_exact = SVector(rho, rho, rho, rho * E, rho * qv, rho * ql) - - du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - - source_terms_moist_bubble(u_exact, x, t, equations)) - # du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1 += rho_x - du2 += rho_x + dp - du3 += rho_x + dp - du4 += dE + 2 * dp - du5 += qv * rho_x - du6 += ql * rho_x - - return SVector(du1, du2, du3, du4, du5, du6) - end - - # Gravity source term - @inline function source_terms_geopotential(u, x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) - end - - @inline function source_terms_geopotential(u, - equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) - end - - # Raylight damping sponge source term form A. Sridhar et al., - # Large-eddy simulations with ClimateMachine: a new open-sourcecode for - # atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, - # https://arxiv.org/abs/2110.00853 [physics.ao-ph] . - @inline function source_terms_nonhydrostatic_raylight_sponge(u, x, t, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - z = x[2] - - # relaxed background velocity - vr1, vr2 = (10.0, 0.0) - # damping threshold - z_s = 9000.0 - # boundary top - z_top = 16000.0 - # positive even power with default value 2 - gamma = 2.0 - # relaxation coefficient > 0 - alpha = 0.5 - - tau_s = zero(eltype(u)) - if z > z_s - tau_s = alpha * sin(0.5 * (z - z_s) * inv(z_top - z_s))^(gamma) - end - - return SVector(zero(eltype(u)), - -tau_s * rho * (v1 - vr1), - -tau_s * rho * (v2 - vr2), - zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) - end - - # Source term with gravity and phase change - @inline function source_terms_moist_bubble(u, x, t, - equations::CompressibleMoistEulerEquations2D) - return source_terms_geopotential(u, equations) + - source_terms_phase_change(u, equations) - end - - # Calculate pressure and temperature from a state u. - @inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # Inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - - return SVector(p, T) - end - - # Calculate Q_ph for a state u. - # This source term models the phase chance between could water and vapor. - @inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, _, _, _, rho_qv, rho_ql = u - _, T = get_current_condition(u, equations) - rho_qd = rho - rho_qv - rho_ql - - T_C = T - 273.15 - # saturation vapor pressure - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - - # saturation density of vapor - rho_star_qv = p_vs / (R_v * T) - - # Fisher-Burgmeister-Function - a = rho_star_qv - rho_qv - b = rho - rho_qv - rho_qd - - # saturation control factor - # < 1: stronger saturation effect - # > 1: weaker saturation effect - C = 1 - - return (a + b - sqrt(a^2 + b^2)) * C - end - - # Add the source containing Q_ph - @inline function source_terms_phase_change(u, - equations::CompressibleMoistEulerEquations2D) - Q_ph = phase_change_term(u, equations) - - return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), - zero(eltype(u)), Q_ph, -Q_ph) - end - - # Low Mach number approximate Riemann solver (LMARS) from - # X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. - # Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian - # Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, - # https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. - @inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack a = equations - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr - p_ll, T_ll = get_current_condition(u_ll, equations) - p_rr, T_rr = get_current_condition(u_rr, equations) - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - - v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # diffusion parameter <= 1 - beta = 1 - - # Compute the necessary interface flux components - norm_ = norm(normal_direction) - - rho = 0.5 * (rho_ll + rho_rr) - p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ - v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ - - if (v_interface > 0) - f1, f2, f3, f4, f5, f6 = u_ll * v_interface - f4 += p_ll * v_interface - else - f1, f2, f3, f4, f5, f6 = u_rr * v_interface - f4 += p_rr * v_interface - end - - return SVector(f1, - f2 + p_interface * normal_direction[1], - f3 + p_interface * normal_direction[2], - f4, f5, f6) - end - - # Convert conservative variables to primitive. - @inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = get_current_condition(u, equations)[1] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, p, qv, ql) - end - - # Convert conservative variables to primitive with - # temperature instead of pressure. - @inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - T = get_current_condition(u, equations)[2] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, T, qv, ql) - end - - # Convert conservative variables to entropy - @inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p, T = get_current_condition(u, equations) - v_square = v1^2 + v2^2 - rho_qd = rho - rho_qv - rho_ql - - # Workaround if an individual density is zero - # Thermodynamic entropy - s_d = 0 - s_v = 0 - s_l = 0 - - # Thermodynamic entropy - if (rho_qd > 0.0) - s_d = c_pd * log(T) - R_d * log(rho_qd * R_d * T) - end - if (rho_qv > 0.0) - s_v = c_pv * log(T) - R_v * log(rho_qv * R_v * T) - end - if (rho_ql > 0.0) - s_l = c_pl * log(T) - end - - g_d = (c_pd - s_d) * T - g_v = L_00 + (c_pv - s_v) * T - g_l = (c_pl - s_l) * T - - w1 = g_d - 0.5 * v_square - w2 = v1 - w3 = v2 - w4 = -1 - w5 = g_v - g_d - w6 = g_l - g_d - - return inv(T) * SVector(w1, w2, w3, w4, w5, w6) - end - - # Convert primitive to conservative variables. - @inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) - rho, v1, v2, p, qv, ql = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_qv = rho * qv - rho_ql = rho * ql - rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) - return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) - end - - # Convert conservative variables to primitive with - # potential temperature instead of pressure. - @inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = dry_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end - - # Convert conservative variables to primitive with - # moist potential temperature instead of pressure. - @inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = moist_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end - - # Convert conservative variables to moisture related variables. - @inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v * inv(p_vs) - - rho_d = rho - (rho_qv + rho_ql) - r_v = inv(rho_d) * rho_qv - r_l = inv(rho_d) * rho_ql - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - - pot1 = qv - pot2 = ql - pot3 = r_v + r_l - pot4 = T - pot5 = H - pot6 = equivalent_pottemp_thermodynamic(u, equations) - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end - - @inline function density(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - return rho - end - - @inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) - rho_qd = u[1] - (u[5] + u[6]) - return rho_qd - end - - @inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho_qv = u[5] - return rho_qv - end - - @inline function temperature(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - return T - end - - @inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - T = get_current_condition(u, equations)[2] - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - return H - end - - @inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho_ql = u[6] - return rho_ql - end - - @inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_ql = u[6] - ql = rho_ql / rho - return ql - end - - @inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_qv = u[5] - qv = rho_qv / rho - return qv - end - - @inline function pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - return p - end - - @inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_times_p = rho * get_current_condition(u, equations)[1] - return rho_times_p - end - - # Calculate thermodynamic entropy for a conservative state `cons`. - # This is the dryspecific entropy multiplied by the dry air density. - @inline function entropy_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, c_vv, c_pl, R_d, R_v = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_qd = cons[1] - cons[5] - cons[6] - rho_qv = cons[5] - rho_ql = cons[6] - # Thermodynamic entropy - s_d = c_vd * log(T) - R_d * log(rho_qd * R_d) - - s_v = c_vv * log(T) - R_v * log(rho_qv * R_v) - - s_l = c_pl * log(T) - - return rho_qd * s_d + rho_qv * s_v + rho_ql * s_l - end - - # Calculate mathematical entropy for a conservative state `cons`. - @inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) - - return S - end - - # Default entropy is the mathematical entropy. - @inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, - equations) - - # Calculate total energy for a conservative state `cons`. - @inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] - - # Calculate kinetic energy for a conservative state `cons`. - @inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) - end - - # Calculate internal energy for a conservative state `cons`. - @inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) - return energy_total(cons, equations) - energy_kinetic(cons, equations) - end - - # Calculate the dry potential temperature for a conservative state `cons`. - @inline function dry_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, p_0, kappa = equations - # Pressure - p = get_current_condition(cons, equations)[1] - # Potential temperature - pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) - - return pot - end - - # Calculate the moist potential temperature for a conservative state `cons`. - @inline function moist_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_d = cons[1] - (cons[5] + cons[6]) - r_v = inv(rho_d) * cons[5] - r_l = inv(rho_d) * cons[6] - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - return pot - end - - # Calculate the equivalent potential temperature for a conservative state `cons`. - @inline function equivalent_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - # equivalent potential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - return aeq_pot - end - - # Convert conservative variables to primitive varuables with - # equivalent potential temperature instead of pressure - # and mixing ratios innstead of specific contend. - @inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - # equivalent potential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = aeq_pot - pot5 = r_v - pot6 = r_t - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end - - @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * - inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * - inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) - - # Compute the sound speeds on the left and right - v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) - end - - # Adjusted version of LLF dissipation from compressible euler. - @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * - inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * - inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) - # Calculate normal velocities and sound speed - # left - v_ll = (v1_ll * normal_direction[1] - + - v2_ll * normal_direction[2]) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - # right - v_rr = (v1_rr * normal_direction[1] - + - v2_rr * normal_direction[2]) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) - end - - # Adjusted version of lambda_max from compressible euler. - @inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho, v1, v2, p, qv, ql = cons2prim(u, equations) - qd = 1 - qv - ql - - gamma = (qd * c_pd + qv * c_pv + ql * c_pl) * inv(qd * c_vd + qv * c_vv + ql * c_pl) - c = sqrt(gamma * p / rho) - - return (abs(v1) + c, abs(v2) + c) - end - - # Adjusted EC flux in a normal direction with R_q=0. This is based on - # A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the - # multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, - # https://arxiv.org/abs/1904.00972 [math.NA]. - @inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - R_q = 0 - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr - - rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll - rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr - v1_ll = rho_v1_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_ll = rho_v2_ll / rho_ll - v2_rr = rho_v2_rr / rho_rr - - # inner energy - rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) - rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) - - # Absolute Temperature - T_ll = (rho_e_ll - L_00 * rho_qv_ll) / - (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) - T_rr = (rho_e_rr - L_00 * rho_qv_rr) / - (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) - - # Compute the necessary mean values - rho_qd_mean = 0 - rho_qv_mean = 0 - rho_ql_mean = 0 - inv_T_mean = 0 - if (!(rho_qd_ll == 0.0) && !(rho_qd_rr == 0.0)) - rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) - end - if (!(rho_qv_ll == 0.0) && !(rho_qv_rr == 0.0)) - rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) - end - if (!(rho_ql_ll == 0.0) && !(rho_ql_rr == 0.0)) - rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) - end - if (!(inv(T_ll) == 0.0) && !(inv(T_rr) == 0.0)) - inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) - end - - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) - v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) - rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) - rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) - rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) - inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) - v_dot_n_avg = normal_direction[1] * v1_avg + normal_direction[2] * v2_avg - - p_int = inv(inv_T_avg) * (R_d * rho_qd_avg + R_v * rho_qv_avg + R_q * rho_ql_avg) - K_avg = 0.5 * (v1_square_avg + v2_square_avg) - - f_1d = rho_qd_mean * v_dot_n_avg - f_1v = rho_qv_mean * v_dot_n_avg - f_1l = rho_ql_mean * v_dot_n_avg - f1 = f_1d + f_1v + f_1l - f2 = f1 * v1_avg + normal_direction[1] * p_int - f3 = f1 * v2_avg + normal_direction[2] * p_int - f4 = ((c_vd * inv_T_mean - K_avg) * f_1d + - (L_00 + c_vv * inv_T_mean - K_avg) * f_1v + - (c_pl * inv_T_mean - K_avg) * f_1l + v1_avg * f2 + v2_avg * f3) - - return SVector(f1, f2, f3, f4, f_1v, f_1l) - end - - varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", - "rho_v2", "rho_E", - "rho_qv", - "rho_ql") - varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", - "p", "qv", "ql") - varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", - "T", "qv", "ql") - varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "drypottemp", - "qv", "ql") - varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "moistpottemp", - "qv", "ql") - varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", - "T", "H", - "aeqpottemp") - varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "aeqpottemp", - "rv", "rt") -end # @muladd From 01084ad6e9fd348f21699af04a5fb85f6ce7063d Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Wed, 24 Jul 2024 08:58:31 +0200 Subject: [PATCH 10/17] check allocations --- test/test_2d_moist_euler.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index 87a1c5e..f43a855 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -34,7 +34,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory u_ode = sol.u[end] du_ode = similar(u_ode) analysis_callback(sol) - @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 2000 + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. end end @@ -67,7 +67,7 @@ end u_ode = sol.u[end] du_ode = similar(u_ode) analysis_callback(sol) - @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 2000 + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. end end From 10bf10d88d819be0360f9942b1da9855afd8fec2 Mon Sep 17 00:00:00 2001 From: Benedict Geihe Date: Wed, 24 Jul 2024 09:15:09 +0200 Subject: [PATCH 11/17] fix errors introduced during merge --- test/Project.toml | 1 + test/runtests.jl | 2 +- test/test_2d_moist_euler.jl | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/Project.toml b/test/Project.toml index 9a3d021..70152e3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,4 +1,5 @@ [deps] +NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Trixi = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb" diff --git a/test/runtests.jl b/test/runtests.jl index 108f2f1..c59a4fa 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,7 +13,7 @@ const TRIXIATMO_NTHREADS = clamp(Sys.CPU_THREADS, 2, 3) include("test_trixi_consistency.jl") end - @time if TRIXI_TEST == "all" || TRIXI_TEST == "moist_euler" + @time if TRIXIATMO_TEST == "all" || TRIXIATMO_TEST == "moist_euler" include("test_2d_moist_euler.jl") end end diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index f43a855..b8ec8af 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -7,7 +7,7 @@ include("test_trixiatmo.jl") # TODO - This is a repetition from Trixi.jl EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory for examples? -@trixi_testset "elixir_moist_euler_dry_bubble.jl" begin +@trixiatmo_testset "elixir_moist_euler_dry_bubble.jl" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_dry_bubble.jl"), l2=[ 1.300428671901329e-6, @@ -39,7 +39,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory end end -@trixi_testset "elixir_moist_euler_EC_bubble" begin +@trixiatmo_testset "elixir_moist_euler_EC_bubble" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_EC_bubble.jl"), l2=[ 0.01345154393018332, From f870c3ad0e7a8076f9e73f8f5f4ee506c1739b24 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Wed, 24 Jul 2024 13:03:10 +0530 Subject: [PATCH 12/17] Remove a TODO --- test/test_2d_moist_euler.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index b8ec8af..fdcb321 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -35,7 +35,6 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory du_ode = similar(u_ode) analysis_callback(sol) @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 - # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. end end @@ -68,7 +67,6 @@ end du_ode = similar(u_ode) analysis_callback(sol) @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 - # TODO - It performs 1920 allocations. Is that okay? All tests in Trixi take 1000. end end From 1e9b68205f96326182818cd4c92b11da63b80d0a Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Tue, 6 Aug 2024 18:03:38 +0530 Subject: [PATCH 13/17] Add all tests --- ...oist_euler_nonhydrostatic_gravity_waves.jl | 10 +- test/test_2d_moist_euler.jl | 123 +++++++++++++++++- 2 files changed, 126 insertions(+), 7 deletions(-) diff --git a/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl index b3b87c3..02ab73e 100644 --- a/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl +++ b/examples/elixir_moist_euler_nonhydrostatic_gravity_waves.jl @@ -2,7 +2,7 @@ using OrdinaryDiffEq using Trixi, TrixiAtmo using TrixiAtmo: CompressibleMoistEulerEquations2D, source_terms_geopotential, source_terms_phase_change, - source_terms_nonhydrostatic_raylight_sponge, + source_terms_nonhydrostatic_rayleigh_sponge, cons2drypot, flux_LMARS ############################################################################### @@ -10,7 +10,7 @@ using TrixiAtmo: CompressibleMoistEulerEquations2D, source_terms_geopotential, # Mountain Triggered Gravity Wave test from: # W. A. Gallus JR., J. B. Klemp, Behavior of Flow over Step Orography, Monthly Weather -# Review Vol. 128.4, pages 1153–1164, 2000, +# Review Vol. 128.4, pages 1153–1164, 2000, # https://doi.org/10.1175/1520-0493(2000)128<1153:BOFOSO>2.0.CO;2. function initial_condition_nonhydrostatic_gravity_wave(x, t, equations::CompressibleMoistEulerEquations2D) @@ -42,7 +42,7 @@ initial_condition = initial_condition_nonhydrostatic_gravity_wave function source(u, x, t, equations::CompressibleMoistEulerEquations2D) return (source_terms_geopotential(u, equations) + source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) + - source_terms_nonhydrostatic_raylight_sponge(u, x, t, + source_terms_nonhydrostatic_rayleigh_sponge(u, x, t, equations::CompressibleMoistEulerEquations2D)) end @@ -62,7 +62,7 @@ volume_integral = VolumeIntegralFluxDifferencing(volume_flux) solver = DGSEM(basis, surface_flux, volume_integral) -# Deformed rectangle that has "witch of agnesi" as bottom +# Deformed rectangle that has "witch of agnesi" as bottom function bottom(x) h = 400.0 @@ -85,7 +85,7 @@ f4(s) = SVector(20000.0 * s, 16000.0) faces = (f1, f2, f3, f4) -# dx = 0.2*a dz = 10-200 m für (40,16) km +# dx = 0.2*a dz = 10-200 m für (40,16) km cells_per_dimension = (100, 80) mesh = StructuredMesh(cells_per_dimension, faces, periodicity = (true, false)) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index fdcb321..dd87492 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -33,7 +33,6 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory t = sol.t[end] u_ode = sol.u[end] du_ode = similar(u_ode) - analysis_callback(sol) @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 end end @@ -65,7 +64,127 @@ end t = sol.t[end] u_ode = sol.u[end] du_ode = similar(u_ode) - analysis_callback(sol) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 + end +end + +@trixiatmo_testset "elixir_moist_euler_moist_bubble" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_moist_bubble.jl"), + l2=[ + 7.351043427240923e-6, 1.1070342432069074e-7, + 0.0006974588377288118, 1.715668353329522, 8.831269143134121e-7, + 1.025579538944668e-6, + ], + linf=[ + 8.055695643149896e-5, 1.1985203677080201e-6, + 0.005897639251024437, 19.24776030163048, 1.0043133039065386e-5, + 1.1439046776775402e-5, + ], + polydeg=3, + cells_per_dimension=(16, 8), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 + end +end + +@trixiatmo_testset "elixir_moist_euler_nonhydrostatic_gravity_waves" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_moist_euler_nonhydrostatic_gravity_waves.jl"), + l2=[ + 3.5420405147937216e-5, 0.0021265774152361538, + 0.01172830034500581, 9.898560584459009, 0.0, 0.0, + ], + linf=[ + 0.0017602202683439927, 0.14941973735192882, 0.5945327351674782, + 489.89171406268724, 0.0, 0.0, + ], + polydeg=3, + cells_per_dimension=(10, 8), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 + end +end + +@trixiatmo_testset "elixir_moist_euler_source_terms_dry" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_source_terms_dry.jl"), + l2=[ + 1.3992076791281227e-5, 1.4486417486907815e-5, + 2.609465609739115e-5, 6.323484379066432e-5, 0.0, 0.0, + ], + linf=[ + 7.549984224430872e-5, 0.00010065352517929504, + 0.00015964938767742964, 0.0005425860570698049, 0.0, 0.0, + ], + polydeg=3, + cells_per_dimension=(10, 8), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 + end +end + +@trixiatmo_testset "elixir_moist_euler_source_terms_moist" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_source_terms_moist.jl"), + l2=[ + 1.8307663991129928e-5, 0.04008077097727512, + 0.015104690877128945, 0.5242098451814421, 5.474006801215573e-10, + 1.1103883907226752e-10, + ], + linf=[ + 0.00013219484616722177, 0.10771224937484702, + 0.03789645369775574, 3.90888311638264, 3.938382289041286e-9, + 6.892033377287209e-10, + ], + polydeg=3, + cells_per_dimension=(10, 8), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) + @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 + end +end + +@trixiatmo_testset "elixir_moist_euler_source_terms_split_moist" begin + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_moist_euler_source_terms_split_moist.jl"), + l2=[ + 0.0001480393848825987, 0.11945481031503036, 0.07460345535073129, + 5.943049264963717, 4.471792794168725e-9, 7.10320253652373e-10, + ], + linf=[ + 0.0007084183215528839, 0.5047962996690205, 0.3697160082709827, + 27.843155286573165, 2.1168438904322837e-8, 3.691699932047233e-9, + ], + polydeg=3, + cells_per_dimension=(10, 8), + tspan=(0.0, 0.1)) + # Ensure that we do not have excessive memory allocations + # (e.g., from type instabilities) + let + t = sol.t[end] + u_ode = sol.u[end] + du_ode = similar(u_ode) @test (@allocated TrixiAtmo.Trixi.rhs!(du_ode, u_ode, semi, t)) < 100 end end From d146b8cd247a947b7a8ce510f8652c3a85d8b813 Mon Sep 17 00:00:00 2001 From: Arpit-Babbar Date: Tue, 6 Aug 2024 18:05:56 +0530 Subject: [PATCH 14/17] Improve formatting --- test/test_2d_moist_euler.jl | 76 +++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index dd87492..6bcb8c5 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -71,13 +71,19 @@ end @trixiatmo_testset "elixir_moist_euler_moist_bubble" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_moist_bubble.jl"), l2=[ - 7.351043427240923e-6, 1.1070342432069074e-7, - 0.0006974588377288118, 1.715668353329522, 8.831269143134121e-7, + 7.351043427240923e-6, + 1.1070342432069074e-7, + 0.0006974588377288118, + 1.715668353329522, + 8.831269143134121e-7, 1.025579538944668e-6, ], linf=[ - 8.055695643149896e-5, 1.1985203677080201e-6, - 0.005897639251024437, 19.24776030163048, 1.0043133039065386e-5, + 8.055695643149896e-5, + 1.1985203677080201e-6, + 0.005897639251024437, + 19.24776030163048, + 1.0043133039065386e-5, 1.1439046776775402e-5, ], polydeg=3, @@ -97,12 +103,20 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_nonhydrostatic_gravity_waves.jl"), l2=[ - 3.5420405147937216e-5, 0.0021265774152361538, - 0.01172830034500581, 9.898560584459009, 0.0, 0.0, + 3.5420405147937216e-5, + 0.0021265774152361538, + 0.01172830034500581, + 9.898560584459009, + 0.0, + 0.0, ], linf=[ - 0.0017602202683439927, 0.14941973735192882, 0.5945327351674782, - 489.89171406268724, 0.0, 0.0, + 0.0017602202683439927, + 0.14941973735192882, + 0.5945327351674782, + 489.89171406268724, + 0.0, + 0.0, ], polydeg=3, cells_per_dimension=(10, 8), @@ -120,12 +134,20 @@ end @trixiatmo_testset "elixir_moist_euler_source_terms_dry" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_source_terms_dry.jl"), l2=[ - 1.3992076791281227e-5, 1.4486417486907815e-5, - 2.609465609739115e-5, 6.323484379066432e-5, 0.0, 0.0, + 1.3992076791281227e-5, + 1.4486417486907815e-5, + 2.609465609739115e-5, + 6.323484379066432e-5, + 0.0, + 0.0, ], linf=[ - 7.549984224430872e-5, 0.00010065352517929504, - 0.00015964938767742964, 0.0005425860570698049, 0.0, 0.0, + 7.549984224430872e-5, + 0.00010065352517929504, + 0.00015964938767742964, + 0.0005425860570698049, + 0.0, + 0.0, ], polydeg=3, cells_per_dimension=(10, 8), @@ -143,13 +165,19 @@ end @trixiatmo_testset "elixir_moist_euler_source_terms_moist" begin @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_source_terms_moist.jl"), l2=[ - 1.8307663991129928e-5, 0.04008077097727512, - 0.015104690877128945, 0.5242098451814421, 5.474006801215573e-10, + 1.8307663991129928e-5, + 0.04008077097727512, + 0.015104690877128945, + 0.5242098451814421, + 5.474006801215573e-10, 1.1103883907226752e-10, ], linf=[ - 0.00013219484616722177, 0.10771224937484702, - 0.03789645369775574, 3.90888311638264, 3.938382289041286e-9, + 0.00013219484616722177, + 0.10771224937484702, + 0.03789645369775574, + 3.90888311638264, + 3.938382289041286e-9, 6.892033377287209e-10, ], polydeg=3, @@ -169,12 +197,20 @@ end @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_source_terms_split_moist.jl"), l2=[ - 0.0001480393848825987, 0.11945481031503036, 0.07460345535073129, - 5.943049264963717, 4.471792794168725e-9, 7.10320253652373e-10, + 0.0001480393848825987, + 0.11945481031503036, + 0.07460345535073129, + 5.943049264963717, + 4.471792794168725e-9, + 7.10320253652373e-10, ], linf=[ - 0.0007084183215528839, 0.5047962996690205, 0.3697160082709827, - 27.843155286573165, 2.1168438904322837e-8, 3.691699932047233e-9, + 0.0007084183215528839, + 0.5047962996690205, + 0.3697160082709827, + 27.843155286573165, + 2.1168438904322837e-8, + 3.691699932047233e-9, ], polydeg=3, cells_per_dimension=(10, 8), From 8cc67bb2ade037f5c15f4dea6b9cddf029581bae Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 16 Aug 2024 17:16:53 +0200 Subject: [PATCH 15/17] resolved issues from merge, cleaned up --- ...xir_euler_spherical_advection_cartesian.jl | 0 .../compressible_moist_euler_2d_lucas.jl | 1967 ++++++++--------- test/test_2d_moist_euler.jl | 28 +- 3 files changed, 996 insertions(+), 999 deletions(-) rename examples/{p4est_2d_dgsem => }/elixir_euler_spherical_advection_cartesian.jl (100%) diff --git a/examples/p4est_2d_dgsem/elixir_euler_spherical_advection_cartesian.jl b/examples/elixir_euler_spherical_advection_cartesian.jl similarity index 100% rename from examples/p4est_2d_dgsem/elixir_euler_spherical_advection_cartesian.jl rename to examples/elixir_euler_spherical_advection_cartesian.jl diff --git a/src/equations/compressible_moist_euler_2d_lucas.jl b/src/equations/compressible_moist_euler_2d_lucas.jl index f9941a3..638cc39 100644 --- a/src/equations/compressible_moist_euler_2d_lucas.jl +++ b/src/equations/compressible_moist_euler_2d_lucas.jl @@ -7,1026 +7,1023 @@ import Trixi: varnames, flux_chandrashekar, boundary_condition_slip_wall, cons2prim, cons2entropy, max_abs_speed_naive, max_abs_speeds, entropy, energy_total, flux -# By default, Julia/LLVM does not use fused multiply-add operations (FMAs). -# Since these FMAs can increase the performance of many numerical algorithms, -# we need to opt-in explicitly. -# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details. @muladd begin - struct CompressibleMoistEulerEquations2D{RealT <: Real} <: - AbstractCompressibleMoistEulerEquations{2, 6} - p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) - c_pd::RealT # dry air constant - c_vd::RealT # dry air constant - R_d::RealT # dry air gas constant - c_pv::RealT # moist air constant - c_vv::RealT # moist air constant - R_v::RealT # moist air gas constant - c_pl::RealT # liqid water constant - g::RealT # gravitation constant - kappa::RealT # ratio of the gas constant R_d - gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications - L_00::RealT # latent heat of evaporation at 0 K - a::RealT +#! format: noindent +struct CompressibleMoistEulerEquations2D{RealT <: Real} <: + AbstractCompressibleMoistEulerEquations{2, 6} + p_0::RealT # constant reference pressure 1000 hPa(100000 Pa) + c_pd::RealT # dry air constant + c_vd::RealT # dry air constant + R_d::RealT # dry air gas constant + c_pv::RealT # moist air constant + c_vv::RealT # moist air constant + R_v::RealT # moist air gas constant + c_pl::RealT # liqid water constant + g::RealT # gravitation constant + kappa::RealT # ratio of the gas constant R_d + gamma::RealT # = inv(kappa- 1); can be used to write slow divisions as fast multiplications + L_00::RealT # latent heat of evaporation at 0 K + a::RealT +end + +function CompressibleMoistEulerEquations2D(; g = 9.81, RealT = Float64) + p_0 = 100000.0 + c_pd = 1004.0 + c_vd = 717.0 + R_d = c_pd - c_vd + c_pv = 1885.0 + c_vv = 1424.0 + R_v = c_pv - c_vv + c_pl = 4186.0 + gamma = c_pd / c_vd # = 1/(1 - kappa) + kappa = 1 - inv(gamma) + L_00 = 3147620.0 + a = 360.0 + return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, + R_v, c_pl, g, kappa, gamma, L_00, a) +end + +# Calculate 1D flux for a single point. +@inline function flux(u, orientation::Integer, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + if orientation == 1 + f1 = rho_v1 + f2 = rho_v1 * v1 + p + f3 = rho_v1 * v2 + f4 = (rho_E + p) * v1 + f5 = rho_v1 * qv + f6 = rho_v1 * ql + else + f1 = rho_v2 + f2 = rho_v2 * v1 + f3 = rho_v2 * v2 + p + f4 = (rho_E + p) * v2 + f5 = rho_v2 * qv + f6 = rho_v2 * ql end - - function CompressibleMoistEulerEquations2D(; g = 9.81, RealT = Float64) - p_0 = 100000.0 - c_pd = 1004.0 - c_vd = 717.0 - R_d = c_pd - c_vd - c_pv = 1885.0 - c_vv = 1424.0 - R_v = c_pv - c_vv - c_pl = 4186.0 - gamma = c_pd / c_vd # = 1/(1 - kappa) - kappa = 1 - inv(gamma) - L_00 = 3147620.0 - a = 360.0 - return CompressibleMoistEulerEquations2D{RealT}(p_0, c_pd, c_vd, R_d, c_pv, c_vv, - R_v, c_pl, g, kappa, gamma, L_00, a) - end - - # Calculate 1D flux for a single point. - @inline function flux(u, orientation::Integer, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - if orientation == 1 - f1 = rho_v1 - f2 = rho_v1 * v1 + p - f3 = rho_v1 * v2 - f4 = (rho_E + p) * v1 - f5 = rho_v1 * qv - f6 = rho_v1 * ql - else - f1 = rho_v2 - f2 = rho_v2 * v1 - f3 = rho_v2 * v2 + p - f4 = (rho_E + p) * v2 - f5 = rho_v2 * qv - f6 = rho_v2 * ql - end - return SVector(f1, f2, f3, f4, f5, f6) - end - - # Calculate 1D flux for a single point in the normal direction. - # Note, this directional vector is not normalized. - @inline function flux(u, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] - rho_v_normal = rho * v_normal - f1 = rho_v_normal - f2 = (rho_v_normal) * v1 + p * normal_direction[1] - f3 = (rho_v_normal) * v2 + p * normal_direction[2] - f4 = (rho_e + p) * v_normal - f5 = rho_v_normal * qv - f6 = (rho_v_normal) * ql - return SVector(f1, f2, f3, f4, f5, f6) + return SVector(f1, f2, f3, f4, f5, f6) +end + +# Calculate 1D flux for a single point in the normal direction. +# Note, this directional vector is not normalized. +@inline function flux(u, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + v_normal = v1 * normal_direction[1] + v2 * normal_direction[2] + rho_v_normal = rho * v_normal + f1 = rho_v_normal + f2 = (rho_v_normal) * v1 + p * normal_direction[1] + f3 = (rho_v_normal) * v2 + p * normal_direction[2] + f4 = (rho_e + p) * v_normal + f5 = rho_v_normal * qv + f6 = (rho_v_normal) * ql + return SVector(f1, f2, f3, f4, f5, f6) +end + +# Slip-wall boundary condition +# Determine the boundary numerical surface flux for a slip wall condition. +# Imposes a zero normal velocity at the wall. +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + x, t, + surface_flux_function, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + norm_ = norm(normal_direction) + # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later + normal = normal_direction / norm_ + + # rotate the internal solution state + u_local = rotate_to_x(u_inner, normal, equations) + + # compute the primitive variables + rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, + equations) + qd_local = 1 - qv_local - ql_local + gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl) * + inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) + # Get the solution of the pressure Riemann problem + # See Section 6.3.3 of + # Eleuterio F. Toro (2009) + # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction + # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) + if v_normal <= 0.0 + sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed + p_star = p_local * + (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * + inv(gamma - 1)) + else # v_normal > 0.0 + A = 2.0 / ((gamma + 1) * rho_local) + B = p_local * (gamma - 1) / (gamma + 1) + p_star = p_local + + 0.5 * v_normal / A * + (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) end - # Slip-wall boundary condition - # Determine the boundary numerical surface flux for a slip wall condition. - # Imposes a zero normal velocity at the wall. - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - x, t, - surface_flux_function, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - norm_ = norm(normal_direction) - # Normalize the vector without using `normalize` since we need to multiply by the `norm_` later - normal = normal_direction / norm_ - - # rotate the internal solution state - u_local = rotate_to_x(u_inner, normal, equations) - - # compute the primitive variables - rho_local, v_normal, v_tangent, p_local, qv_local, ql_local = cons2prim(u_local, - equations) - qd_local = 1 - qv_local - ql_local - gamma = (qd_local * c_pd + qv_local * c_pv + ql_local * c_pl) * - inv(qd_local * c_vd + qv_local * c_vv + ql_local * c_pl) - # Get the solution of the pressure Riemann problem - # See Section 6.3.3 of - # Eleuterio F. Toro (2009) - # Riemann Solvers and Numerical Methods for Fluid Dynamics: A Practical Introduction - # [DOI: 10.1007/b79761](https://doi.org/10.1007/b79761) - if v_normal <= 0.0 - sound_speed = sqrt(gamma * p_local / rho_local) # local sound speed - p_star = p_local * - (1.0 + 0.5 * (gamma - 1) * v_normal / sound_speed)^(2.0 * gamma * - inv(gamma - 1)) - else # v_normal > 0.0 - A = 2.0 / ((gamma + 1) * rho_local) - B = p_local * (gamma - 1) / (gamma + 1) - p_star = p_local + - 0.5 * v_normal / A * - (v_normal + sqrt(v_normal^2 + 4.0 * A * (p_local + B))) - end - - # For the slip wall we directly set the flux as the normal velocity is zero - return SVector(zero(eltype(u_inner)), - p_star * normal[1], - p_star * normal[2], - zero(eltype(u_inner)), - zero(eltype(u_inner)), - zero(eltype(u_inner))) * norm_ + # For the slip wall we directly set the flux as the normal velocity is zero + return SVector(zero(eltype(u_inner)), + p_star * normal[1], + p_star * normal[2], + zero(eltype(u_inner)), + zero(eltype(u_inner)), + zero(eltype(u_inner))) * norm_ +end + +# Fix sign for structured mesh. +@inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, + direction, x, t, + surface_flux_function, + equations::CompressibleMoistEulerEquations2D) + # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back + # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh + if isodd(direction) + boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, + x, t, surface_flux_function, + equations) + else + boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, + x, t, surface_flux_function, + equations) end - # Fix sign for structured mesh. - @inline function boundary_condition_slip_wall(u_inner, normal_direction::AbstractVector, - direction, x, t, - surface_flux_function, + return boundary_flux +end + +# Rotate momentum flux. The same as in compressible Euler. +@inline function rotate_to_x(u, normal_vector::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + # cos and sin of the angle between the x-axis and the normalized normal_vector are + # the normalized vector's x and y coordinates respectively (see unit circle). + c = normal_vector[1] + s = normal_vector[2] + + # Apply the 2D rotation matrix with normal and tangent directions of the form + # [ 1 0 0 0; + # 0 n_1 n_2 0; + # 0 t_1 t_2 0; + # 0 0 0 1 ] + # where t_1 = -n_2 and t_2 = n_1 + + return SVector(u[1], + c * u[2] + s * u[3], + -s * u[2] + c * u[3], + u[4], u[5], u[6]) +end + +# Recreates the convergence test initial condition from compressible euler 2D. +function initial_condition_convergence_test_dry(x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 0 + ql = 0 + + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + + T = (ini - 1) / c_vd + E = (mu * T + qv * L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) +end + +# Recreates the convergence test source term from compressible euler 2D. +@inline function source_terms_convergence_test_dry(u, x, t, + equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + qv = 0 + ql = 0 + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + T = (rho - 1) / c_vd + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) + + du1 += rho_x + du2 += rho_x + dp + du3 += du2 + du4 += dE + 2 * dp + du5 += qv * du1 + du6 += ql * du1 + + return SVector(du1, du2, du3, du4, du5, du6) +end + +# Initial condition for the convergence analysis. +# Extends the convergence test to the system with gravity and phase change. +function initial_condition_convergence_test_moist(x, t, equations::CompressibleMoistEulerEquations2D) - # flip sign of normal to make it outward pointing, then flip the sign of the normal flux back - # to be inward pointing on the -x and -y sides due to the orientation convention used by StructuredMesh - if isodd(direction) - boundary_flux = -boundary_condition_slip_wall(u_inner, -normal_direction, - x, t, surface_flux_function, - equations) - else - boundary_flux = boundary_condition_slip_wall(u_inner, normal_direction, - x, t, surface_flux_function, - equations) - end - - return boundary_flux - end - - # Rotate momentum flux. The same as in compressible Euler. - @inline function rotate_to_x(u, normal_vector::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - # cos and sin of the angle between the x-axis and the normalized normal_vector are - # the normalized vector's x and y coordinates respectively (see unit circle). - c = normal_vector[1] - s = normal_vector[2] - - # Apply the 2D rotation matrix with normal and tangent directions of the form - # [ 1 0 0 0; - # 0 n_1 n_2 0; - # 0 t_1 t_2 0; - # 0 0 0 1 ] - # where t_1 = -n_2 and t_2 = n_1 - - return SVector(u[1], - c * u[2] + s * u[3], - -s * u[2] + c * u[3], - u[4], u[5], u[6]) - end - - # Recreates the convergence test initial condition from compressible euler 2D. - function initial_condition_convergence_test_dry(x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 0 - ql = 0 - - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - - T = (ini - 1) / c_vd - E = (mu * T + qv * L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) - end - - # Recreates the convergence test source term from compressible euler 2D. - @inline function source_terms_convergence_test_dry(u, x, t, - equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - qv = 0 - ql = 0 - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - T = (rho - 1) / c_vd - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1, du2, du3, du4, du5, du6 = source_terms_moist_bubble(u, x, t, equations) - - du1 += rho_x - du2 += rho_x + dp - du3 += du2 - du4 += dE + 2 * dp - du5 += qv * du1 - du6 += ql * du1 - - return SVector(du1, du2, du3, du4, du5, du6) - end - - # Initial condition for the convergence analysis. - # Extends the convergence test to the system with gravity and phase change. - function initial_condition_convergence_test_moist(x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - ini = c + A * sin(ω * (x[1] + x[2] - t)) - - qv = 100 / L_00 - ql = qv / 10 - - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - - T = (ini - 1) / mu + 10 / c_vd + 40 - E = (mu * T + qv * L_00 + 1) - - rho = ini - rho_v1 = ini - rho_v2 = ini - rho_e = E * ini - rho_qv = qv * ini - rho_ql = ql * ini - - return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) - end - - # Source term for the convergence analysis. - # Extends the convergence test to the system with gravity and phase change. - @inline function source_terms_convergence_test_moist(u, x, t, - equations::CompressibleMoistEulerEquations2D) - # Same settings as in `initial_condition` - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - c = 2 - A = 0.1 - L = 2 - f = 1 / L - ω = 2 * pi * f - - x1, x2 = x - si, co = sincos(ω * (x1 + x2 - t)) - rho = c + A * si - rho_x = ω * A * co - - qv = 100 / L_00 - ql = qv / 10 - mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) - xi = ((1 - qv - ql) * R_d + qv * R_v) - - T = (rho - 1) / mu + 10 / c_vd + 40 - dT = rho_x / c_vd - E = (mu * T + qv * L_00 + 1) - dE = E * rho_x + rho * mu * dT - dp = xi * (T * rho_x + rho * dT) - - # Calculate Error in Sources with exact solution and u - u_exact = SVector(rho, rho, rho, rho * E, rho * qv, rho * ql) - - du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - - source_terms_moist_bubble(u_exact, x, t, equations)) - # du1, du2, du3, du4, du5, du6 = zeros(Float64, 6) - # Note that d/dt rho = -d/dx rho = -d/dy rho. - - du1 += rho_x - du2 += rho_x + dp - du3 += rho_x + dp - du4 += dE + 2 * dp - du5 += qv * rho_x - du6 += ql * rho_x - - return SVector(du1, du2, du3, du4, du5, du6) - end - - # Gravity source term - @inline function source_terms_geopotential(u, x, t, - equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) - end - - @inline function source_terms_geopotential(u, - equations::CompressibleMoistEulerEquations2D) - @unpack g = equations - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - tmp = rho_v2 - - return SVector(zero(eltype(u)), zero(eltype(u)), - -g * rho, -g * tmp, - zero(eltype(u)), zero(eltype(u))) - end - - # Rayleigh damping sponge source term form A. Sridhar et al., - # Large-eddy simulations with ClimateMachine: a new open-sourcecode for - # atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, - # https://arxiv.org/abs/2110.00853 [physics.ao-ph] . - @inline function source_terms_nonhydrostatic_rayleigh_sponge(u, x, t, - equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - z = x[2] - - # relaxed background velocity - vr1, vr2 = (10.0, 0.0) - # damping threshold - z_s = 9000.0 - # boundary top - z_top = 16000.0 - # positive even power with default value 2 - gamma = 2.0 - # relaxation coefficient > 0 - alpha = 0.5 - - tau_s = zero(eltype(u)) - if z > z_s - tau_s = alpha * sin(0.5 * (z - z_s) * inv(z_top - z_s))^(gamma) - end - - return SVector(zero(eltype(u)), - -tau_s * rho * (v1 - vr1), - -tau_s * rho * (v2 - vr2), - zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) - end - - # Source term with gravity and phase change - @inline function source_terms_moist_bubble(u, x, t, - equations::CompressibleMoistEulerEquations2D) - return source_terms_geopotential(u, equations) + - source_terms_phase_change(u, equations) - end - - # Calculate pressure and temperature from a state u. - @inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - # Inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - - # Absolute temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - - return SVector(p, T) - end - - # Calculate Q_ph for a state u. - # This source term models the phase chance between could water and vapor. - @inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, _, _, _, rho_qv, rho_ql = u - _, T = get_current_condition(u, equations) - rho_qd = rho - rho_qv - rho_ql - - T_C = T - 273.15 - # saturation vapor pressure - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - - # saturation density of vapor - rho_star_qv = p_vs / (R_v * T) - - # Fisher-Burgmeister-Function - a = rho_star_qv - rho_qv - b = rho - rho_qv - rho_qd - - # saturation control factor - # < 1: stronger saturation effect - # > 1: weaker saturation effect - C = 1 - - return (a + b - sqrt(a^2 + b^2)) * C - end - - # Add the source containing Q_ph - @inline function source_terms_phase_change(u, - equations::CompressibleMoistEulerEquations2D) - Q_ph = phase_change_term(u, equations) - - return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), - zero(eltype(u)), Q_ph, -Q_ph) - end - - # Low Mach number approximate Riemann solver (LMARS) from - # X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. - # Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian - # Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, - # https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. - @inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack a = equations - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr - p_ll, T_ll = get_current_condition(u_ll, equations) - p_rr, T_rr = get_current_condition(u_rr, equations) - v1_ll = rho_v1_ll / rho_ll - v2_ll = rho_v2_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_rr = rho_v2_rr / rho_rr - - v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] - v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] - - # diffusion parameter <= 1 - beta = 1 - - # Compute the necessary interface flux components - norm_ = norm(normal_direction) - - rho = 0.5 * (rho_ll + rho_rr) - p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ - v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ - - if (v_interface > 0) - f1, f2, f3, f4, f5, f6 = u_ll * v_interface - f4 += p_ll * v_interface - else - f1, f2, f3, f4, f5, f6 = u_rr * v_interface - f4 += p_rr * v_interface - end - - return SVector(f1, - f2 + p_interface * normal_direction[1], - f3 + p_interface * normal_direction[2], - f4, f5, f6) - end - - # Convert conservative variables to primitive. - @inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p = get_current_condition(u, equations)[1] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, p, qv, ql) - end - - # Convert conservative variables to primitive with - # temperature instead of pressure. - @inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - v1 = rho_v1 / rho - v2 = rho_v2 / rho - T = get_current_condition(u, equations)[2] - qv = rho_qv / rho - ql = rho_ql / rho - - return SVector(rho, v1, v2, T, qv, ql) - end - - # Convert conservative variables to entropy - @inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - p, T = get_current_condition(u, equations) - v_square = v1^2 + v2^2 - rho_qd = rho - rho_qv - rho_ql - - # Workaround if an individual density is zero - # Thermodynamic entropy - s_d = 0 - s_v = 0 - s_l = 0 - - # Thermodynamic entropy - if (rho_qd > 0.0) - s_d = c_pd * log(T) - R_d * log(rho_qd * R_d * T) - end - if (rho_qv > 0.0) - s_v = c_pv * log(T) - R_v * log(rho_qv * R_v * T) - end - if (rho_ql > 0.0) - s_l = c_pl * log(T) - end - - g_d = (c_pd - s_d) * T - g_v = L_00 + (c_pv - s_v) * T - g_l = (c_pl - s_l) * T - - w1 = g_d - 0.5 * v_square - w2 = v1 - w3 = v2 - w4 = -1 - w5 = g_v - g_d - w6 = g_l - g_d - - return inv(T) * SVector(w1, w2, w3, w4, w5, w6) - end - - # Convert primitive to conservative variables. - @inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) - rho, v1, v2, p, qv, ql = prim - rho_v1 = rho * v1 - rho_v2 = rho * v2 - rho_qv = rho * qv - rho_ql = rho * ql - rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) - return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) - end - - # Convert conservative variables to primitive with - # potential temperature instead of pressure. - @inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = dry_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end - - # Convert conservative variables to primitive with - # moist potential temperature instead of pressure. - @inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = moist_pottemp_thermodynamic(u, equations) - pot5 = qv - pot6 = ql - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + ini = c + A * sin(ω * (x[1] + x[2] - t)) + + qv = 100 / L_00 + ql = qv / 10 + + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + + T = (ini - 1) / mu + 10 / c_vd + 40 + E = (mu * T + qv * L_00 + 1) + + rho = ini + rho_v1 = ini + rho_v2 = ini + rho_e = E * ini + rho_qv = qv * ini + rho_ql = ql * ini + + return SVector(rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql) +end + +# Source term for the convergence analysis. +# Extends the convergence test to the system with gravity and phase change. +@inline function source_terms_convergence_test_moist(u, x, t, + equations::CompressibleMoistEulerEquations2D) + # Same settings as in `initial_condition` + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + c = 2 + A = 0.1 + L = 2 + f = 1 / L + ω = 2 * pi * f + + x1, x2 = x + si, co = sincos(ω * (x1 + x2 - t)) + rho = c + A * si + rho_x = ω * A * co + + qv = 100 / L_00 + ql = qv / 10 + mu = ((1 - qv - ql) * c_vd + qv * c_vv + ql * c_pl) + xi = ((1 - qv - ql) * R_d + qv * R_v) + + T = (rho - 1) / mu + 10 / c_vd + 40 + dT = rho_x / c_vd + E = (mu * T + qv * L_00 + 1) + dE = E * rho_x + rho * mu * dT + dp = xi * (T * rho_x + rho * dT) + + # Calculate Error in Sources with exact solution and u + u_exact = SVector(rho, rho, rho, rho * E, rho * qv, rho * ql) + + du1, du2, du3, du4, du5, du6 = (source_terms_moist_bubble(u, x, t, equations) - + source_terms_moist_bubble(u_exact, x, t, equations)) + + # Note that d/dt rho = -d/dx rho = -d/dy rho. + + du1 += rho_x + du2 += rho_x + dp + du3 += rho_x + dp + du4 += dE + 2 * dp + du5 += qv * rho_x + du6 += ql * rho_x + + return SVector(du1, du2, du3, du4, du5, du6) +end + +# Gravity source term +@inline function source_terms_geopotential(u, x, t, + equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 - # Convert conservative variables to moisture related variables. - @inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - qv = rho_qv / rho - ql = rho_ql / rho - p, T = get_current_condition(u, equations) - - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v * inv(p_vs) - - rho_d = rho - (rho_qv + rho_ql) - r_v = inv(rho_d) * rho_qv - r_l = inv(rho_d) * rho_ql - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - - pot1 = qv - pot2 = ql - pot3 = r_v + r_l - pot4 = T - pot5 = H - pot6 = equivalent_pottemp_thermodynamic(u, equations) - - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) - end + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) +end - @inline function density(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - return rho +@inline function source_terms_geopotential(u, + equations::CompressibleMoistEulerEquations2D) + @unpack g = equations + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + tmp = rho_v2 + + return SVector(zero(eltype(u)), zero(eltype(u)), + -g * rho, -g * tmp, + zero(eltype(u)), zero(eltype(u))) +end + +# Rayleigh damping sponge source term form A. Sridhar et al., +# Large-eddy simulations with ClimateMachine: a new open-sourcecode for +# atmospheric simulations on GPUs and CPUs, 2 Oct 2021, doi: 10.5194/gmd-15-6259-2022, +# https://arxiv.org/abs/2110.00853 [physics.ao-ph] . +@inline function source_terms_nonhydrostatic_rayleigh_sponge(u, x, t, + equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_e, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + z = x[2] + + # relaxed background velocity + vr1, vr2 = (10.0, 0.0) + # damping threshold + z_s = 9000.0 + # boundary top + z_top = 16000.0 + # positive even power with default value 2 + gamma = 2.0 + # relaxation coefficient > 0 + alpha = 0.5 + + tau_s = zero(eltype(u)) + if z > z_s + tau_s = alpha * sin(0.5 * (z - z_s) * inv(z_top - z_s))^(gamma) end - @inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) - rho_qd = u[1] - (u[5] + u[6]) - return rho_qd - end + return SVector(zero(eltype(u)), + -tau_s * rho * (v1 - vr1), + -tau_s * rho * (v2 - vr2), + zero(eltype(u)), zero(eltype(u)), zero(eltype(u))) +end - @inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho_qv = u[5] - return rho_qv - end +# Source term with gravity and phase change +@inline function source_terms_moist_bubble(u, x, t, + equations::CompressibleMoistEulerEquations2D) + return source_terms_geopotential(u, equations) + + source_terms_phase_change(u, equations) +end - @inline function temperature(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql +# Calculate pressure and temperature from a state u. +@inline function get_current_condition(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql - v1 = rho_v1 / rho - v2 = rho_v2 / rho + v1 = rho_v1 / rho + v2 = rho_v2 / rho - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + # Inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - return T - end + # Absolute temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) - @inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack R_v = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - T = get_current_condition(u, equations)[2] - p_v = rho_qv * R_v * T - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - return H - end + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T - @inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho_ql = u[6] - return rho_ql - end + return SVector(p, T) +end - @inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_ql = u[6] - ql = rho_ql / rho - return ql - end +# Calculate Q_ph for a state u. +# This source term models the phase chance between could water and vapor. +@inline function phase_change_term(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, _, _, _, rho_qv, rho_ql = u + _, T = get_current_condition(u, equations) + rho_qd = rho - rho_qv - rho_ql - @inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_qv = u[5] - qv = rho_qv / rho - return qv - end + T_C = T - 273.15 + # saturation vapor pressure + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - @inline function pressure(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - rho_qd = rho - rho_qv - rho_ql + # saturation density of vapor + rho_star_qv = p_vs / (R_v * T) - v1 = rho_v1 / rho - v2 = rho_v2 / rho + # Fisher-Burgmeister-Function + a = rho_star_qv - rho_qv + b = rho - rho_qv - rho_qd - # inner energy - rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + # saturation control factor + # < 1: stronger saturation effect + # > 1: weaker saturation effect + C = 1 - # Absolute Temperature - T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + return (a + b - sqrt(a^2 + b^2)) * C +end - # Pressure - p = (rho_qd * R_d + rho_qv * R_v) * T - return p - end - - @inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) - rho = u[1] - rho_times_p = rho * get_current_condition(u, equations)[1] - return rho_times_p - end - - # Calculate thermodynamic entropy for a conservative state `cons`. - # This is the dryspecific entropy multiplied by the dry air density. - @inline function entropy_thermodynamic(cons, +# Add the source containing Q_ph +@inline function source_terms_phase_change(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_vd, c_vv, c_pl, R_d, R_v = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_qd = cons[1] - cons[5] - cons[6] - rho_qv = cons[5] - rho_ql = cons[6] - # Thermodynamic entropy - s_d = c_vd * log(T) - R_d * log(rho_qd * R_d) - - s_v = c_vv * log(T) - R_v * log(rho_qv * R_v) - - s_l = c_pl * log(T) - - return rho_qd * s_d + rho_qv * s_v + rho_ql * s_l + Q_ph = phase_change_term(u, equations) + + return SVector(zero(eltype(u)), zero(eltype(u)), zero(eltype(u)), + zero(eltype(u)), Q_ph, -Q_ph) +end + +# Low Mach number approximate Riemann solver (LMARS) from +# X. Chen, N. Andronova, B. Van Leer, J. E. Penner, J. P. Boyd, C. Jablonowski, S. +# Lin, A Control-Volume Model of the Compressible Euler Equations with a Vertical Lagrangian +# Coordinate Monthly Weather Review Vol. 141.7, pages 2526–2544, 2013, +# https://journals.ametsoc.org/view/journals/mwre/141/7/mwr-d-12-00129.1.xml. +@inline function flux_LMARS(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack a = equations + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_e_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_e_rr, rho_qv_rr, rho_ql_rr = u_rr + p_ll, T_ll = get_current_condition(u_ll, equations) + p_rr, T_rr = get_current_condition(u_rr, equations) + v1_ll = rho_v1_ll / rho_ll + v2_ll = rho_v2_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_rr = rho_v2_rr / rho_rr + + v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2] + v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2] + + # diffusion parameter <= 1 + beta = 1 + + # Compute the necessary interface flux components + norm_ = norm(normal_direction) + + rho = 0.5 * (rho_ll + rho_rr) + p_interface = 0.5 * (p_ll + p_rr) - beta * 0.5 * a * rho * (v_rr - v_ll) / norm_ + v_interface = 0.5 * (v_ll + v_rr) - beta * 1 / (2 * a * rho) * (p_rr - p_ll) * norm_ + + if (v_interface > 0) + f1, f2, f3, f4, f5, f6 = u_ll * v_interface + f4 += p_ll * v_interface + else + f1, f2, f3, f4, f5, f6 = u_rr * v_interface + f4 += p_rr * v_interface end - # Calculate mathematical entropy for a conservative state `cons`. - @inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) - # Mathematical entropy - S = -entropy_thermodynamic(cons, equations) - - return S + return SVector(f1, + f2 + p_interface * normal_direction[1], + f3 + p_interface * normal_direction[2], + f4, f5, f6) +end + +# Convert conservative variables to primitive. +@inline function cons2prim(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p = get_current_condition(u, equations)[1] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, p, qv, ql) +end + +# Convert conservative variables to primitive with +# temperature instead of pressure. +@inline function cons2temp(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + v1 = rho_v1 / rho + v2 = rho_v2 / rho + T = get_current_condition(u, equations)[2] + qv = rho_qv / rho + ql = rho_ql / rho + + return SVector(rho, v1, v2, T, qv, ql) +end + +# Convert conservative variables to entropy +@inline function cons2entropy(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + p, T = get_current_condition(u, equations) + v_square = v1^2 + v2^2 + rho_qd = rho - rho_qv - rho_ql + + # Workaround if an individual density is zero + # Thermodynamic entropy + s_d = 0 + s_v = 0 + s_l = 0 + + # Thermodynamic entropy + if (rho_qd > 0.0) + s_d = c_pd * log(T) - R_d * log(rho_qd * R_d * T) end - - # Default entropy is the mathematical entropy. - @inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, - equations) - - # Calculate total energy for a conservative state `cons`. - @inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] - - # Calculate kinetic energy for a conservative state `cons`. - @inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u - return (rho_v1^2 + rho_v2^2) / (2 * rho) + if (rho_qv > 0.0) + s_v = c_pv * log(T) - R_v * log(rho_qv * R_v * T) end - - # Calculate internal energy for a conservative state `cons`. - @inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) - return energy_total(cons, equations) - energy_kinetic(cons, equations) - end - - # Calculate the dry potential temperature for a conservative state `cons`. - @inline function dry_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, p_0, kappa = equations - # Pressure - p = get_current_condition(cons, equations)[1] - # Potential temperature - pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) - - return pot - end - - # Calculate the moist potential temperature for a conservative state `cons`. - @inline function moist_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations - # Pressure - p, T = get_current_condition(cons, equations) - rho_d = cons[1] - (cons[5] + cons[6]) - r_v = inv(rho_d) * cons[5] - r_l = inv(rho_d) * cons[6] - - # Potential temperature - R_m = R_d + r_v * R_v - c_pml = c_pd + r_v * c_pv + r_l * c_pl - kappa_m = R_m * inv(c_pml) - pot = T * (p_0 / p)^(kappa_m) - return pot - end - - # Calculate the equivalent potential temperature for a conservative state `cons`. - @inline function equivalent_pottemp_thermodynamic(cons, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - # equivalent potential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - return aeq_pot - end - - # Convert conservative variables to primitive varuables with - # equivalent potential temperature instead of pressure - # and mixing ratios innstead of specific contend. - @inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations - rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons - rho_d = rho - rho_qv - rho_ql - p, T = get_current_condition(cons, equations) - p_v = rho_qv * R_v * T - p_d = p - p_v - T_C = T - 273.15 - p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) - H = p_v / p_vs - r_v = rho_qv / rho_d - r_l = rho_ql / rho_d - r_t = r_v + r_l - L_v = L_00 + (c_pv - c_pl) * T - c_p = c_pd + r_t * c_pl - - # equivalent potential temperature - aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * - exp(L_v * r_v * inv(c_p * T))) - - v1 = rho_v1 / rho - v2 = rho_v2 / rho - - pot1 = rho - pot2 = v1 - pot3 = v2 - pot4 = aeq_pot - pot5 = r_v - pot6 = r_t - return SVector(pot1, pot2, pot3, pot4, pot5, pot6) + if (rho_ql > 0.0) + s_l = c_pl * log(T) end - @inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * - inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * - inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) - - # Compute the sound speeds on the left and right - v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - λ_max = max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) + g_d = (c_pd - s_d) * T + g_v = L_00 + (c_pv - s_v) * T + g_l = (c_pl - s_l) * T + + w1 = g_d - 0.5 * v_square + w2 = v1 + w3 = v2 + w4 = -1 + w5 = g_v - g_d + w6 = g_l - g_d + + return inv(T) * SVector(w1, w2, w3, w4, w5, w6) +end + +# Convert primitive to conservative variables. +@inline function prim2cons(prim, equations::CompressibleMoistEulerEquations2D) + rho, v1, v2, p, qv, ql = prim + rho_v1 = rho * v1 + rho_v2 = rho * v2 + rho_qv = rho * qv + rho_ql = rho * ql + rho_E = p * equations.inv_gamma_minus_one + 0.5 * (rho_v1 * v1 + rho_v2 * v2) + return SVector(rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql) +end + +# Convert conservative variables to primitive with +# potential temperature instead of pressure. +@inline function cons2drypot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = dry_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + +# Convert conservative variables to primitive with +# moist potential temperature instead of pressure. +@inline function cons2moistpot(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = moist_pottemp_thermodynamic(u, equations) + pot5 = qv + pot6 = ql + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + +# Convert conservative variables to moisture related variables. +@inline function cons2moist(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + qv = rho_qv / rho + ql = rho_ql / rho + p, T = get_current_condition(u, equations) + + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v * inv(p_vs) + + rho_d = rho - (rho_qv + rho_ql) + r_v = inv(rho_d) * rho_qv + r_l = inv(rho_d) * rho_ql + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + + pot1 = qv + pot2 = ql + pot3 = r_v + r_l + pot4 = T + pot5 = H + pot6 = equivalent_pottemp_thermodynamic(u, equations) + + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + +@inline function density(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + return rho +end + +@inline function density_dry(u, equations::CompressibleMoistEulerEquations2D) + rho_qd = u[1] - (u[5] + u[6]) + return rho_qd +end + +@inline function density_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho_qv = u[5] + return rho_qv +end + +@inline function temperature(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + return T +end + +@inline function saturation_pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack R_v = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + T = get_current_condition(u, equations)[2] + p_v = rho_qv * R_v * T + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + return H +end + +@inline function density_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho_ql = u[6] + return rho_ql +end + +@inline function ratio_liquid(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_ql = u[6] + ql = rho_ql / rho + return ql +end + +@inline function ratio_vapor(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_qv = u[5] + qv = rho_qv / rho + return qv +end + +@inline function pressure(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, R_d, c_vv, c_pv, R_v, c_pl, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + rho_qd = rho - rho_qv - rho_ql + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + # inner energy + rho_e = (rho_E - 0.5 * (rho_v1 * v1 + rho_v2 * v2)) + + # Absolute Temperature + T = (rho_e - L_00 * rho_qv) / (rho_qd * c_vd + rho_qv * c_vv + rho_ql * c_pl) + + # Pressure + p = (rho_qd * R_d + rho_qv * R_v) * T + return p +end + +@inline function density_pressure(u, equations::CompressibleMoistEulerEquations2D) + rho = u[1] + rho_times_p = rho * get_current_condition(u, equations)[1] + return rho_times_p +end + +# Calculate thermodynamic entropy for a conservative state `cons`. +# This is the dryspecific entropy multiplied by the dry air density. +@inline function entropy_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack c_vd, c_vv, c_pl, R_d, R_v = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_qd = cons[1] - cons[5] - cons[6] + rho_qv = cons[5] + rho_ql = cons[6] + # Thermodynamic entropy + s_d = c_vd * log(T) - R_d * log(rho_qd * R_d) + + s_v = c_vv * log(T) - R_v * log(rho_qv * R_v) + + s_l = c_pl * log(T) + + return rho_qd * s_d + rho_qv * s_v + rho_ql * s_l +end + +# Calculate mathematical entropy for a conservative state `cons`. +@inline function entropy_math(cons, equations::CompressibleMoistEulerEquations2D) + # Mathematical entropy + S = -entropy_thermodynamic(cons, equations) + + return S +end + +# Default entropy is the mathematical entropy. +@inline entropy(cons, equations::CompressibleMoistEulerEquations2D) = entropy_math(cons, + equations) + +# Calculate total energy for a conservative state `cons`. +@inline energy_total(cons, ::CompressibleMoistEulerEquations2D) = cons[4] + +# Calculate kinetic energy for a conservative state `cons`. +@inline function energy_kinetic(u, equations::CompressibleMoistEulerEquations2D) + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = u + return (rho_v1^2 + rho_v2^2) / (2 * rho) +end + +# Calculate internal energy for a conservative state `cons`. +@inline function energy_internal(cons, equations::CompressibleMoistEulerEquations2D) + return energy_total(cons, equations) - energy_kinetic(cons, equations) +end + +# Calculate the dry potential temperature for a conservative state `cons`. +@inline function dry_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, p_0, kappa = equations + # Pressure + p = get_current_condition(cons, equations)[1] + # Potential temperature + pot = p_0 * (p / p_0)^(1 - kappa) / (R_d * cons[1]) + + return pot +end + +# Calculate the moist potential temperature for a conservative state `cons`. +@inline function moist_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_pd, c_pv, c_pl, p_0 = equations + # Pressure + p, T = get_current_condition(cons, equations) + rho_d = cons[1] - (cons[5] + cons[6]) + r_v = inv(rho_d) * cons[5] + r_l = inv(rho_d) * cons[6] + + # Potential temperature + R_m = R_d + r_v * R_v + c_pml = c_pd + r_v * c_pv + r_l * c_pl + kappa_m = R_m * inv(c_pml) + pot = T * (p_0 / p)^(kappa_m) + return pot +end + +# Calculate the equivalent potential temperature for a conservative state `cons`. +@inline function equivalent_pottemp_thermodynamic(cons, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, kappa, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + # equivalent potential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + return aeq_pot +end + +# Convert conservative variables to primitive varuables with +# equivalent potential temperature instead of pressure +# and mixing ratios innstead of specific contend. +@inline function cons2aeqpot(cons, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, R_d, R_v, p_0, L_00 = equations + rho, rho_v1, rho_v2, rho_E, rho_qv, rho_ql = cons + rho_d = rho - rho_qv - rho_ql + p, T = get_current_condition(cons, equations) + p_v = rho_qv * R_v * T + p_d = p - p_v + T_C = T - 273.15 + p_vs = 611.2 * exp(17.62 * T_C / (243.12 + T_C)) + H = p_v / p_vs + r_v = rho_qv / rho_d + r_l = rho_ql / rho_d + r_t = r_v + r_l + L_v = L_00 + (c_pv - c_pl) * T + c_p = c_pd + r_t * c_pl + + # equivalent potential temperature + aeq_pot = (T * (p_0 / p_d)^(R_d / c_p) * H^(-r_v * R_v / c_p) * + exp(L_v * r_v * inv(c_p * T))) + + v1 = rho_v1 / rho + v2 = rho_v2 / rho + + pot1 = rho + pot2 = v1 + pot3 = v2 + pot4 = aeq_pot + pot5 = r_v + pot6 = r_t + return SVector(pot1, pot2, pot3, pot4, pot5, pot6) +end + +@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * + inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * + inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) + + # Compute the sound speeds on the left and right + v_mag_ll = sqrt(v1_ll^2 + v2_ll^2) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + v_mag_rr = sqrt(v1_rr^2 + v2_rr^2) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + return max(v_mag_ll, v_mag_rr) + max(c_ll, c_rr) +end + +# Adjusted version of LLF dissipation from compressible euler. +@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) + rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) + qd_ll = 1 - qv_ll - ql_ll + qd_rr = 1 - qv_rr - ql_rr + # Get the density and gas gamma + gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * + inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) + gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * + inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) + # Calculate normal velocities and sound speed + # left + v_ll = (v1_ll * normal_direction[1] + + + v2_ll * normal_direction[2]) + c_ll = sqrt(gamma_ll * p_ll / rho_ll) + # right + v_rr = (v1_rr * normal_direction[1] + + + v2_rr * normal_direction[2]) + c_rr = sqrt(gamma_rr * p_rr / rho_rr) + + return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) +end + +# Adjusted version of lambda_max from compressible euler. +@inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) + @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations + rho, v1, v2, p, qv, ql = cons2prim(u, equations) + qd = 1 - qv - ql + + gamma = (qd * c_pd + qv * c_pv + ql * c_pl) * inv(qd * c_vd + qv * c_vv + ql * c_pl) + c = sqrt(gamma * p / rho) + + return (abs(v1) + c, abs(v2) + c) +end + +# Adjusted EC flux in a normal direction with R_q=0. This is based on +# A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the +# multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, +# https://arxiv.org/abs/1904.00972 [math.NA]. +@inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, + equations::CompressibleMoistEulerEquations2D) + @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations + R_q = 0 + # Unpack left and right state + rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll + rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr + + rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll + rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr + v1_ll = rho_v1_ll / rho_ll + v1_rr = rho_v1_rr / rho_rr + v2_ll = rho_v2_ll / rho_ll + v2_rr = rho_v2_rr / rho_rr + + # inner energy + rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) + rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) + + # Absolute Temperature + T_ll = (rho_e_ll - L_00 * rho_qv_ll) / + (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) + T_rr = (rho_e_rr - L_00 * rho_qv_rr) / + (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) + + # Compute the necessary mean values + rho_qd_mean = 0 + rho_qv_mean = 0 + rho_ql_mean = 0 + inv_T_mean = 0 + if (!(rho_qd_ll == 0.0) && !(rho_qd_rr == 0.0)) + rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) end - - # Adjusted version of LLF dissipation from compressible euler. - @inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho_ll, v1_ll, v2_ll, p_ll, qv_ll, ql_ll = cons2prim(u_ll, equations) - rho_rr, v1_rr, v2_rr, p_rr, qv_rr, ql_rr = cons2prim(u_rr, equations) - qd_ll = 1 - qv_ll - ql_ll - qd_rr = 1 - qv_rr - ql_rr - # Get the density and gas gamma - gamma_ll = (qd_ll * c_pd + qv_ll * c_pv + ql_ll * c_pl) * - inv(qd_ll * c_vd + qv_ll * c_vv + ql_ll * c_pl) - gamma_rr = (qd_rr * c_pd + qv_rr * c_pv + ql_rr * c_pl) * - inv(qd_rr * c_vd + qv_rr * c_vv + ql_rr * c_pl) - # Calculate normal velocities and sound speed - # left - v_ll = (v1_ll * normal_direction[1] - + - v2_ll * normal_direction[2]) - c_ll = sqrt(gamma_ll * p_ll / rho_ll) - # right - v_rr = (v1_rr * normal_direction[1] - + - v2_rr * normal_direction[2]) - c_rr = sqrt(gamma_rr * p_rr / rho_rr) - - return max(abs(v_ll), abs(v_rr)) + max(c_ll, c_rr) * norm(normal_direction) + if (!(rho_qv_ll == 0.0) && !(rho_qv_rr == 0.0)) + rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) end - - # Adjusted version of lambda_max from compressible euler. - @inline function max_abs_speeds(u, equations::CompressibleMoistEulerEquations2D) - @unpack c_pd, c_pv, c_pl, c_vd, c_vv = equations - rho, v1, v2, p, qv, ql = cons2prim(u, equations) - qd = 1 - qv - ql - - gamma = (qd * c_pd + qv * c_pv + ql * c_pl) * inv(qd * c_vd + qv * c_vv + ql * c_pl) - c = sqrt(gamma * p / rho) - - return (abs(v1) + c, abs(v2) + c) + if (!(rho_ql_ll == 0.0) && !(rho_ql_rr == 0.0)) + rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) end - - # Adjusted EC flux in a normal direction with R_q=0. This is based on - # A. Gouasmi, K. Duraisamy, S. M. Murman, Formulation of Entropy-Stable schemes for the - # multicomponent compressible Euler equations, 4 Feb 2020, doi:10.1016/j.cma.2020.112912, - # https://arxiv.org/abs/1904.00972 [math.NA]. - @inline function flux_chandrashekar(u_ll, u_rr, normal_direction::AbstractVector, - equations::CompressibleMoistEulerEquations2D) - @unpack R_d, R_v, c_vd, c_vv, c_pl, L_00 = equations - R_q = 0 - # Unpack left and right state - rho_ll, rho_v1_ll, rho_v2_ll, rho_E_ll, rho_qv_ll, rho_ql_ll = u_ll - rho_rr, rho_v1_rr, rho_v2_rr, rho_E_rr, rho_qv_rr, rho_ql_rr = u_rr - - rho_qd_ll = rho_ll - rho_qv_ll - rho_ql_ll - rho_qd_rr = rho_rr - rho_qv_rr - rho_ql_rr - v1_ll = rho_v1_ll / rho_ll - v1_rr = rho_v1_rr / rho_rr - v2_ll = rho_v2_ll / rho_ll - v2_rr = rho_v2_rr / rho_rr - - # inner energy - rho_e_ll = (rho_E_ll - 0.5 * (rho_v1_ll * v1_ll + rho_v2_ll * v2_ll)) - rho_e_rr = (rho_E_rr - 0.5 * (rho_v1_rr * v1_rr + rho_v2_rr * v2_rr)) - - # Absolute Temperature - T_ll = (rho_e_ll - L_00 * rho_qv_ll) / - (rho_qd_ll * c_vd + rho_qv_ll * c_vv + rho_ql_ll * c_pl) - T_rr = (rho_e_rr - L_00 * rho_qv_rr) / - (rho_qd_rr * c_vd + rho_qv_rr * c_vv + rho_ql_rr * c_pl) - - # Compute the necessary mean values - rho_qd_mean = 0 - rho_qv_mean = 0 - rho_ql_mean = 0 - inv_T_mean = 0 - if (!(rho_qd_ll == 0.0) && !(rho_qd_rr == 0.0)) - rho_qd_mean = ln_mean(rho_qd_ll, rho_qd_rr) - end - if (!(rho_qv_ll == 0.0) && !(rho_qv_rr == 0.0)) - rho_qv_mean = ln_mean(rho_qv_ll, rho_qv_rr) - end - if (!(rho_ql_ll == 0.0) && !(rho_ql_rr == 0.0)) - rho_ql_mean = ln_mean(rho_ql_ll, rho_ql_rr) - end - if (!(inv(T_ll) == 0.0) && !(inv(T_rr) == 0.0)) - inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) - end - - v1_avg = 0.5 * (v1_ll + v1_rr) - v2_avg = 0.5 * (v2_ll + v2_rr) - v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) - v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) - rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) - rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) - rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) - inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) - v_dot_n_avg = normal_direction[1] * v1_avg + normal_direction[2] * v2_avg - - p_int = inv(inv_T_avg) * (R_d * rho_qd_avg + R_v * rho_qv_avg + R_q * rho_ql_avg) - K_avg = 0.5 * (v1_square_avg + v2_square_avg) - - f_1d = rho_qd_mean * v_dot_n_avg - f_1v = rho_qv_mean * v_dot_n_avg - f_1l = rho_ql_mean * v_dot_n_avg - f1 = f_1d + f_1v + f_1l - f2 = f1 * v1_avg + normal_direction[1] * p_int - f3 = f1 * v2_avg + normal_direction[2] * p_int - f4 = ((c_vd * inv_T_mean - K_avg) * f_1d + - (L_00 + c_vv * inv_T_mean - K_avg) * f_1v + - (c_pl * inv_T_mean - K_avg) * f_1l + v1_avg * f2 + v2_avg * f3) - - return SVector(f1, f2, f3, f4, f_1v, f_1l) + if (!(inv(T_ll) == 0.0) && !(inv(T_rr) == 0.0)) + inv_T_mean = inv_ln_mean(inv(T_ll), inv(T_rr)) end - varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", - "rho_v2", "rho_E", - "rho_qv", - "rho_ql") - varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", - "p", "qv", "ql") - varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", - "T", "qv", "ql") - varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "drypottemp", - "qv", "ql") - varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "moistpottemp", - "qv", "ql") - varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", - "T", "H", - "aeqpottemp") - varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", - "v2", - "aeqpottemp", - "rv", "rt") + v1_avg = 0.5 * (v1_ll + v1_rr) + v2_avg = 0.5 * (v2_ll + v2_rr) + v1_square_avg = 0.5 * (v1_ll^2 + v1_rr^2) + v2_square_avg = 0.5 * (v2_ll^2 + v2_rr^2) + rho_qd_avg = 0.5 * (rho_qd_ll + rho_qd_rr) + rho_qv_avg = 0.5 * (rho_qv_ll + rho_qv_rr) + rho_ql_avg = 0.5 * (rho_ql_ll + rho_ql_rr) + inv_T_avg = 0.5 * (inv(T_ll) + inv(T_rr)) + v_dot_n_avg = normal_direction[1] * v1_avg + normal_direction[2] * v2_avg + + p_int = inv(inv_T_avg) * (R_d * rho_qd_avg + R_v * rho_qv_avg + R_q * rho_ql_avg) + K_avg = 0.5 * (v1_square_avg + v2_square_avg) + + f_1d = rho_qd_mean * v_dot_n_avg + f_1v = rho_qv_mean * v_dot_n_avg + f_1l = rho_ql_mean * v_dot_n_avg + f1 = f_1d + f_1v + f_1l + f2 = f1 * v1_avg + normal_direction[1] * p_int + f3 = f1 * v2_avg + normal_direction[2] * p_int + f4 = ((c_vd * inv_T_mean - K_avg) * f_1d + + (L_00 + c_vv * inv_T_mean - K_avg) * f_1v + + (c_pl * inv_T_mean - K_avg) * f_1l + v1_avg * f2 + v2_avg * f3) + + return SVector(f1, f2, f3, f4, f_1v, f_1l) +end + +varnames(::typeof(cons2cons), ::CompressibleMoistEulerEquations2D) = ("rho", "rho_v1", + "rho_v2", "rho_E", + "rho_qv", + "rho_ql") +varnames(::typeof(cons2prim), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", + "p", "qv", "ql") +varnames(::typeof(cons2temp), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", "v2", + "T", "qv", "ql") +varnames(::typeof(cons2drypot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "drypottemp", + "qv", "ql") +varnames(::typeof(cons2moistpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "moistpottemp", + "qv", "ql") +varnames(::typeof(cons2moist), ::CompressibleMoistEulerEquations2D) = ("qv", "ql", "rt", + "T", "H", + "aeqpottemp") +varnames(::typeof(cons2aeqpot), ::CompressibleMoistEulerEquations2D) = ("rho", "v1", + "v2", + "aeqpottemp", + "rv", "rt") end # @muladd diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index 6bcb8c5..85185f0 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -15,7 +15,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory 0.0006660124630171347, 0.008969786054960861, 0.0, - 0.0, + 0.0 ], linf=[ 1.0312042909910168e-5, @@ -23,7 +23,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory 0.006392107590872236, 0.07612038028310053, 0.0, - 0.0, + 0.0 ], polydeg=3, tspan=(0.0, 0.1)) @@ -45,7 +45,7 @@ end 7.938812668709457, 4500.747616411578, 0.00015592413050814787, - 0.00014163475049532796, + 0.00014163475049532796 ], linf=[ 0.1427479052298466, @@ -53,7 +53,7 @@ end 91.56822550162855, 49528.383866247605, 0.0019364397602254623, - 0.0013259689889851285, + 0.0013259689889851285 ], polydeg=3, cells_per_dimension=(16, 16), @@ -76,7 +76,7 @@ end 0.0006974588377288118, 1.715668353329522, 8.831269143134121e-7, - 1.025579538944668e-6, + 1.025579538944668e-6 ], linf=[ 8.055695643149896e-5, @@ -84,7 +84,7 @@ end 0.005897639251024437, 19.24776030163048, 1.0043133039065386e-5, - 1.1439046776775402e-5, + 1.1439046776775402e-5 ], polydeg=3, cells_per_dimension=(16, 8), @@ -108,7 +108,7 @@ end 0.01172830034500581, 9.898560584459009, 0.0, - 0.0, + 0.0 ], linf=[ 0.0017602202683439927, @@ -116,7 +116,7 @@ end 0.5945327351674782, 489.89171406268724, 0.0, - 0.0, + 0.0 ], polydeg=3, cells_per_dimension=(10, 8), @@ -139,7 +139,7 @@ end 2.609465609739115e-5, 6.323484379066432e-5, 0.0, - 0.0, + 0.0 ], linf=[ 7.549984224430872e-5, @@ -147,7 +147,7 @@ end 0.00015964938767742964, 0.0005425860570698049, 0.0, - 0.0, + 0.0 ], polydeg=3, cells_per_dimension=(10, 8), @@ -170,7 +170,7 @@ end 0.015104690877128945, 0.5242098451814421, 5.474006801215573e-10, - 1.1103883907226752e-10, + 1.1103883907226752e-10 ], linf=[ 0.00013219484616722177, @@ -178,7 +178,7 @@ end 0.03789645369775574, 3.90888311638264, 3.938382289041286e-9, - 6.892033377287209e-10, + 6.892033377287209e-10 ], polydeg=3, cells_per_dimension=(10, 8), @@ -202,7 +202,7 @@ end 0.07460345535073129, 5.943049264963717, 4.471792794168725e-9, - 7.10320253652373e-10, + 7.10320253652373e-10 ], linf=[ 0.0007084183215528839, @@ -210,7 +210,7 @@ end 0.3697160082709827, 27.843155286573165, 2.1168438904322837e-8, - 3.691699932047233e-9, + 3.691699932047233e-9 ], polydeg=3, cells_per_dimension=(10, 8), From bf8c23e7b1c4ca05ba3d0defd74054b25d812781 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 16 Aug 2024 17:27:52 +0200 Subject: [PATCH 16/17] still not formatted? --- test/test_2d_moist_euler.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index 85185f0..4a78632 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -8,7 +8,8 @@ include("test_trixiatmo.jl") # TODO - This is a repetition from Trixi.jl EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory for examples? @trixiatmo_testset "elixir_moist_euler_dry_bubble.jl" begin - @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_moist_euler_dry_bubble.jl"), + @test_trixi_include(joinpath(EXAMPLES_DIR, + "elixir_moist_euler_dry_bubble.jl"), l2=[ 1.300428671901329e-6, 2.601090012108739e-5, From 5f91de5b1340ca16789fff6f92c6bb02f61106c3 Mon Sep 17 00:00:00 2001 From: Tristan Montoya Date: Fri, 16 Aug 2024 17:52:37 +0200 Subject: [PATCH 17/17] ok, now I think the formatting is fixed --- test/test_2d_moist_euler.jl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/test_2d_moist_euler.jl b/test/test_2d_moist_euler.jl index 4a78632..81c965e 100644 --- a/test/test_2d_moist_euler.jl +++ b/test/test_2d_moist_euler.jl @@ -16,7 +16,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory 0.0006660124630171347, 0.008969786054960861, 0.0, - 0.0 + 0.0, ], linf=[ 1.0312042909910168e-5, @@ -24,7 +24,7 @@ EXAMPLES_DIR = pkgdir(TrixiAtmo, "examples") # TODO - Do we need a subdirectory 0.006392107590872236, 0.07612038028310053, 0.0, - 0.0 + 0.0, ], polydeg=3, tspan=(0.0, 0.1)) @@ -46,7 +46,7 @@ end 7.938812668709457, 4500.747616411578, 0.00015592413050814787, - 0.00014163475049532796 + 0.00014163475049532796, ], linf=[ 0.1427479052298466, @@ -54,7 +54,7 @@ end 91.56822550162855, 49528.383866247605, 0.0019364397602254623, - 0.0013259689889851285 + 0.0013259689889851285, ], polydeg=3, cells_per_dimension=(16, 16), @@ -77,7 +77,7 @@ end 0.0006974588377288118, 1.715668353329522, 8.831269143134121e-7, - 1.025579538944668e-6 + 1.025579538944668e-6, ], linf=[ 8.055695643149896e-5, @@ -85,7 +85,7 @@ end 0.005897639251024437, 19.24776030163048, 1.0043133039065386e-5, - 1.1439046776775402e-5 + 1.1439046776775402e-5, ], polydeg=3, cells_per_dimension=(16, 8), @@ -109,7 +109,7 @@ end 0.01172830034500581, 9.898560584459009, 0.0, - 0.0 + 0.0, ], linf=[ 0.0017602202683439927, @@ -117,7 +117,7 @@ end 0.5945327351674782, 489.89171406268724, 0.0, - 0.0 + 0.0, ], polydeg=3, cells_per_dimension=(10, 8), @@ -140,7 +140,7 @@ end 2.609465609739115e-5, 6.323484379066432e-5, 0.0, - 0.0 + 0.0, ], linf=[ 7.549984224430872e-5, @@ -148,7 +148,7 @@ end 0.00015964938767742964, 0.0005425860570698049, 0.0, - 0.0 + 0.0, ], polydeg=3, cells_per_dimension=(10, 8), @@ -171,7 +171,7 @@ end 0.015104690877128945, 0.5242098451814421, 5.474006801215573e-10, - 1.1103883907226752e-10 + 1.1103883907226752e-10, ], linf=[ 0.00013219484616722177, @@ -179,7 +179,7 @@ end 0.03789645369775574, 3.90888311638264, 3.938382289041286e-9, - 6.892033377287209e-10 + 6.892033377287209e-10, ], polydeg=3, cells_per_dimension=(10, 8), @@ -203,7 +203,7 @@ end 0.07460345535073129, 5.943049264963717, 4.471792794168725e-9, - 7.10320253652373e-10 + 7.10320253652373e-10, ], linf=[ 0.0007084183215528839, @@ -211,7 +211,7 @@ end 0.3697160082709827, 27.843155286573165, 2.1168438904322837e-8, - 3.691699932047233e-9 + 3.691699932047233e-9, ], polydeg=3, cells_per_dimension=(10, 8),