Skip to content

Commit

Permalink
Merge pull request #412 from firedrakeproject/moist_thermal_sw
Browse files Browse the repository at this point in the history
PR #412: add physics and an example for moist thermal shallow-water
  • Loading branch information
tommbendall authored Jul 28, 2023
2 parents 3fcb482 + d382306 commit bb74025
Show file tree
Hide file tree
Showing 5 changed files with 515 additions and 13 deletions.
146 changes: 146 additions & 0 deletions examples/shallow_water/moist_thermal_williamson5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"""
Moist flow over a mountain test case from Zerroukat and Allen (2015). Similar
to the Williamson et al test 5 but with additional thermodynamic equations.
"""
from gusto import *
from firedrake import (IcosahedralSphereMesh, SpatialCoordinate,
as_vector, pi, sqrt, min_value, exp, cos)
import sys

# ----------------------------------------------------------------- #
# Test case parameters
# ----------------------------------------------------------------- #

dt = 300

if '--running-tests' in sys.argv:
tmax = dt
dumpfreq = 1
else:
day = 24*60*60
tmax = 50*day
ndumps = 50
dumpfreq = int(tmax / (ndumps*dt))

R = 6371220.
H = 5960.
u_max = 20.
# moist shallow water parameters
epsilon = 1/300
SP = -40*epsilon
EQ = 30*epsilon
NP = -20*epsilon
mu1 = 0.05
mu2 = 0.98
L_v = 10
q0 = 135 # chosen to give an initial max vapour of approx 0.02
beta2 = 1
qprecip = 10e-4
gamma_r = 10e-3
# topography parameters
R0 = pi/9.
R0sq = R0**2
lamda_c = -pi/2.
phi_c = pi/6.

# ----------------------------------------------------------------- #
# Set up model objects
# ----------------------------------------------------------------- #

# Domain
mesh = IcosahedralSphereMesh(radius=R,
refinement_level=4, degree=1)
degree = 1
domain = Domain(mesh, dt, "BDM", degree)
x = SpatialCoordinate(mesh)

# Equation
parameters = ShallowWaterParameters(H=H)
Omega = parameters.Omega
fexpr = 2*Omega*x[2]/R

# Topography
phi, lamda = latlon_coords(mesh)
lsq = (lamda - lamda_c)**2
thsq = (phi - phi_c)**2
rsq = min_value(R0sq, lsq+thsq)
r = sqrt(rsq)
tpexpr = 2000 * (1 - r/R0)

tracers = [WaterVapour(space='DG'), CloudWater(space='DG'), Rain(space='DG')]
eqns = ShallowWaterEquations(domain, parameters, fexpr=fexpr, bexpr=tpexpr,
thermal=True,
active_tracers=tracers)

# I/O
dirname = "moist_thermal_williamson5"
output = OutputParameters(dirname=dirname,
dumplist_latlon=['D'],
dumpfreq=dumpfreq,
log_level='INFO')
diagnostic_fields = [Sum('D', 'topography'), CourantNumber()]
io = IO(domain, output, diagnostic_fields=diagnostic_fields)


# Saturation function
def sat_func(x_in):
h = x_in.split()[1]
b = x_in.split()[2]
return (q0/(g*h + g*tpexpr)) * exp(20*(1 - b/g))


# Feedback proportionality is dependent on h and b
def gamma_v(x_in):
h = x_in.split()[1]
b = x_in.split()[2]
return (1 + L_v*(20*q0/(g*h + g*tpexpr) * exp(20*(1 - b/g))))**(-1)


SWSaturationAdjustment(eqns, sat_func, L_v, time_varying_saturation=True,
parameters=parameters, thermal_feedback=True,
beta2=beta2, gamma_v=gamma_v,
time_varying_gamma_v=True)

InstantRain(eqns, qprecip, vapour_name="cloud_water", rain_name="rain",
gamma_r=gamma_r)

# Timestepper
stepper = Timestepper(eqns, RK4(domain), io)

# ----------------------------------------------------------------- #
# Initial conditions
# ----------------------------------------------------------------- #

u0 = stepper.fields("u")
D0 = stepper.fields("D")
b0 = stepper.fields("b")
v0 = stepper.fields("water_vapour")
c0 = stepper.fields("cloud_water")
r0 = stepper.fields("rain")

uexpr = as_vector([-u_max*x[1]/R, u_max*x[0]/R, 0.0])

g = parameters.g
Rsq = R**2
Dexpr = H - ((R * Omega * u_max + 0.5*u_max**2)*x[2]**2/Rsq)/g - tpexpr

# Expression for initial buoyancy - note the bracket around 1-mu
F = (2/(pi**2))*(phi*(phi-pi/2)*SP - 2*(phi+pi/2)*(phi-pi/2)*(1-mu1)*EQ + phi*(phi+pi/2)*NP)
theta_expr = F + mu1*EQ*cos(phi)*sin(lamda)
bexpr = g * (1 - theta_expr)

# Expression for initial vapour depends on initial saturation
initial_msat = q0/(g*D0 + g*tpexpr) * exp(20*theta_expr)
vexpr = mu2 * initial_msat

# Initialise (cloud and rain initially zero)
u0.project(uexpr)
D0.interpolate(Dexpr)
b0.interpolate(bexpr)
v0.interpolate(vexpr)

# ----------------------------------------------------------------- #
# Run
# ----------------------------------------------------------------- #

stepper.run(t=0, tmax=tmax)
7 changes: 4 additions & 3 deletions examples/shallow_water/thermal_williamson2.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
ShallowWaterKineticEnergy(),
ShallowWaterPotentialEnergy(params),
ShallowWaterPotentialEnstrophy(),
SteadyStateError('u'), SteadyStateError('D')]
SteadyStateError('u'), SteadyStateError('D'),
MeridionalComponent('u'), ZonalComponent('u')]
io = IO(domain, output, diagnostic_fields=diagnostic_fields)
transport_methods = [DGUpwind(eqns, "u"),
DGUpwind(eqns, "D"),
Expand All @@ -74,11 +75,11 @@
uexpr = sphere_to_cartesian(mesh, u_max*cos(phi), 0)
g = params.g
w = Omega*R*u_max + (u_max**2)/2
sigma = 0
sigma = w/10

Dexpr = H - (1/g)*(w + sigma)*((sin(phi))**2)

numerator = theta_0 - sigma*((cos(phi))**2) * ((w + sigma)*(cos(phi))**2 + 2*(phi_0 - w - sigma))
numerator = theta_0 + sigma*((cos(phi))**2) * ((w + sigma)*(cos(phi))**2 + 2*(phi_0 - w - sigma))

denominator = phi_0**2 + (w + sigma)**2*(sin(phi))**4 - 2*phi_0*(w + sigma)*(sin(phi))**2

Expand Down
Loading

0 comments on commit bb74025

Please sign in to comment.