Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thermal sw #573

Draft
wants to merge 27 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0995958
separate out thermal shallow water in to its own class
jshipton Nov 13, 2024
507fd49
equivalent buoyancy formulation
jshipton Nov 14, 2024
4bda2e7
bugfixes
jshipton Nov 14, 2024
616958c
fixes and linear thermal she class
jshipton Nov 14, 2024
6e6d4e8
fix lint
jshipton Nov 14, 2024
329b4b5
maybe actually fix lint
jshipton Nov 14, 2024
3134522
remove unnecessary thermal arg
jshipton Nov 14, 2024
db9b0cf
this seems to be a more robust way of defining field names and space …
jshipton Nov 14, 2024
ec1a5d4
fix lint
jshipton Nov 14, 2024
cc3adef
fix topography terms
jshipton Nov 14, 2024
3f843ce
be careful with buoyancy name
jshipton Nov 14, 2024
6c827c2
first attempt at improving docstrongs
jshipton Nov 14, 2024
a9083e8
linear thermal Galewsky jet example
nhartney Nov 14, 2024
9f37899
Merge branch 'thermal_sw' of https://github.com/firedrakeproject/gust…
nhartney Nov 14, 2024
b48f4e1
very minor typo!
jshipton Nov 18, 2024
b4cf09d
Merge branch 'thermal_sw' of https://github.com/firedrakeproject/gust…
jshipton Nov 18, 2024
b667682
bugfix to compute_saturation
jshipton Nov 18, 2024
074c27a
make compute_saturation a method of the thermal shallow water class
jshipton Nov 18, 2024
b4713dc
add comment to compute_saturation
jshipton Nov 18, 2024
11cb249
maybe this is less icky
jshipton Nov 18, 2024
d807f26
fix lint and indentation bug
jshipton Nov 19, 2024
dfb3ec8
expunge bexpr
jshipton Nov 19, 2024
7392984
first draft of equivalent buoyancy formulation linear solver - one te…
jshipton Nov 19, 2024
4ec629f
expunge bexpr from examples
jshipton Nov 19, 2024
3f220f8
make equivalent buoyancy formulation run
jshipton Nov 25, 2024
021e019
an attempt to linearise the thermal shallow water equations
jshipton Nov 26, 2024
2424344
define qvbar
jshipton Nov 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions examples/shallow_water/linear_thermal_galewsky_jet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from gusto import *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realise this example has just been added here for now and maybe you're still planning to do this, but we will need to make it match the format of the other examples to ensure that it gets run through the testing -- i.e. it will need a __main__ method and use arg-parse so that it can be called from the command line with different arguments.

To be consistent with everything else, we'd also need a plotting script and a figure, although I think we could set up an issue and do that in a different PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did just put this here for now but I can make those changes if we decide we want this example to stay.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be a nice example to have -- I think we should have something with the linear thermal equations

from firedrake import IcosahedralSphereMesh, Constant, ge, le, exp, cos, \
conditional, interpolate, SpatialCoordinate, VectorFunctionSpace, \
Function, assemble, dx, pi

import numpy as np

# ----------------------------------------------------------------- #
# Test case parameters
# ----------------------------------------------------------------- #
day = 24.*60.*60.
tmax = 6*day
dt = 2000
ndumps = 24
ref = 3
# Shallow water parameters
R = 6371220.
H = 10000.
parameters = ShallowWaterParameters(H=H)

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

# Domain
mesh = IcosahedralSphereMesh(radius=R,
refinement_level=ref, degree=2)
x = SpatialCoordinate(mesh)

domain = Domain(mesh, dt, 'BDM', 1)

# Equation
Omega = parameters.Omega
fexpr = 2*Omega*x[2]/R
eqns = LinearThermalShallowWaterEquations(domain, parameters, fexpr=fexpr)

dirname = "linear_thermal_galewsky_jet"
dumpfreq = int(tmax / (ndumps*dt))
output = OutputParameters(dirname=dirname,
dumpfreq=1,
dumplist_latlon=['D', 'b',
'PotentialVorticity',
'RelativeVorticity'],
dump_nc=True,
dump_vtus=True,
chkptfreq=1)

diagnostic_fields = [PotentialVorticity(), RelativeVorticity(), CourantNumber()]
io = IO(domain, output, diagnostic_fields=diagnostic_fields)

transport_methods = [DefaultTransport(eqns, "D")]

stepper = Timestepper(eqns, RK4(domain), io, spatial_methods=transport_methods)

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

u0 = stepper.fields("u")
D0 = stepper.fields("D")
b0 = stepper.fields("b")

# get lat lon coordinates
lamda, phi, _ = lonlatr_from_xyz(x[0], x[1], x[2])

# expressions for meridional and zonal velocity
u_max = 80.0
phi0 = pi/7.
phi1 = pi/2. - phi0
en = np.exp(-4./((phi1-phi0)**2))
u_zonal_expr = (u_max/en)*exp(1/((phi - phi0)*(phi - phi1)))
u_zonal = conditional(ge(phi, phi0), conditional(le(phi, phi1), u_zonal_expr, 0.), 0.)
u_merid = 0.0

# get cartesian components of velocity
uexpr = xyz_vector_from_lonlatr(u_zonal, 0, 0, x)

# expression for buoyancy
g = Constant(parameters.g)
bexpr = g - cos(phi)
b0.interpolate(bexpr)

# ----------------------------------------------------------------------- #
# Compute balanced initial depth - this code based on the dry Galewsky jet
# ----------------------------------------------------------------------- #


def D_integrand(th):
# Initial D field is calculated by integrating D_integrand w.r.t. phi
# Assumes the input is between phi0 and phi1.
# Note that this function operates on vectorized input.
from numpy import exp, sin, tan
f = 2.0*parameters.Omega*sin(th)
u_zon = (80.0/en)*exp(1.0/((th - phi0)*(th - phi1)))
return u_zon*(f + tan(th)*u_zon/R)


def Dval(X):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use our NumericalIntegral code here, e.g. like this galewsky jet case study: https://github.com/firedrakeproject/gusto_case_studies/blob/main/case_studies/shallow_water/galewsky_jet.py#L128

# Function to return value of D at X
from scipy import integrate

# Preallocate output array
val = np.zeros(len(X))

angles = np.zeros(len(X))

# Minimize work by only calculating integrals for points with
# phi between phi_0 and phi_1.
# For phi <= phi_0, the integral is 0
# For phi >= phi_1, the integral is constant.

# Precalculate this constant:
poledepth, _ = integrate.fixed_quad(D_integrand, phi0, phi1, n=64)
poledepth *= -R/parameters.g

angles[:] = np.arcsin(X[:, 2]/R)

for ii in range(len(X)):
if angles[ii] <= phi0:
val[ii] = 0.0
elif angles[ii] >= phi1:
val[ii] = poledepth
else:
# Fixed quadrature with 64 points gives absolute errors below 1e-13
# for a quantity of order 1e-3.
v, _ = integrate.fixed_quad(D_integrand, phi0, angles[ii], n=64)
val[ii] = -(R/parameters.g)*v

return val


def initialise_fn():
u0 = stepper.fields("u")
D0 = stepper.fields("D")

u0.project(uexpr, form_compiler_parameters={'quadrature_degree': 12})

# Get coordinates to pass to Dval function
W = VectorFunctionSpace(mesh, D0.ufl_element())

X = interpolate(mesh.coordinates, W)
D0.dat.data[:] = Dval(X.dat.data_ro)
D0.interpolate(D0 - (H/(2*g) * b0))

# Adjust mean value of initial D
C = Function(D0.function_space()).assign(Constant(1.0))
area = assemble(C*dx)
Dmean = assemble(D0*dx)/area
D0 -= Dmean
D0 += Constant(parameters.H)


initialise_fn()

# Set reference profiles
Dbar = Function(D0.function_space()).assign(H)
bbar = Function(b0.function_space()).interpolate(g)
stepper.set_reference_profiles([('D', Dbar), ('b', bbar)])

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

stepper.run(t=0, tmax=20*dt)
6 changes: 3 additions & 3 deletions examples/shallow_water/moist_thermal_williamson_5.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from gusto import (
Domain, IO, OutputParameters, Timestepper, RK4, DGUpwind,
ShallowWaterParameters, ShallowWaterEquations, Sum,
ShallowWaterParameters, ThermalShallowWaterEquations, Sum,
lonlatr_from_xyz, InstantRain, SWSaturationAdjustment, WaterVapour,
CloudWater, Rain, GeneralIcosahedralSphereMesh, RelativeVorticity,
ZonalComponent, MeridionalComponent
Expand Down Expand Up @@ -99,8 +99,8 @@ def moist_thermal_williamson_5(
tracers = [
WaterVapour(space='DG'), CloudWater(space='DG'), Rain(space='DG')
]
eqns = ShallowWaterEquations(
domain, parameters, fexpr=fexpr, bexpr=tpexpr, thermal=True,
eqns = ThermalShallowWaterEquations(
domain, parameters, fexpr=fexpr, bexpr=tpexpr,
active_tracers=tracers, u_transport_option=u_eqn_type
)

Expand Down
6 changes: 3 additions & 3 deletions examples/shallow_water/thermal_williamson_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from firedrake import Function, SpatialCoordinate, sin, cos
from gusto import (
Domain, IO, OutputParameters, SemiImplicitQuasiNewton, SSPRK3, DGUpwind,
TrapeziumRule, ShallowWaterParameters, ShallowWaterEquations,
TrapeziumRule, ShallowWaterParameters, ThermalShallowWaterEquations,
RelativeVorticity, PotentialVorticity, SteadyStateError,
ZonalComponent, MeridionalComponent, ThermalSWSolver,
xyz_vector_from_lonlatr, lonlatr_from_xyz, GeneralIcosahedralSphereMesh
Expand Down Expand Up @@ -70,8 +70,8 @@ def thermal_williamson_2(
params = ShallowWaterParameters(H=mean_depth, g=g)
Omega = params.Omega
fexpr = 2*Omega*z/radius
eqns = ShallowWaterEquations(
domain, params, fexpr=fexpr, u_transport_option=u_eqn_type, thermal=True
eqns = ThermalShallowWaterEquations(
domain, params, fexpr=fexpr, u_transport_option=u_eqn_type
)

# IO
Expand Down
9 changes: 9 additions & 0 deletions gusto/core/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ class ShallowWaterParameters(Configuration):
g = 9.80616
Omega = 7.292e-5 # rotation rate
H = None # mean depth
# Factor that multiplies the vapour in the equivalent buoyancy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these parameters (beta2, nu and q0) are all features of having moisture rather than the equivalent buoyancy, so could we omit the part about the equivalent buoyancy formulation in these comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm the only time we get the parameters from here is when we use the equivalent buoyancy formulation... @nhartney does it make sense to get the moisture parameters from here in other cases? Are there more parameters that could go here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't make sense to get the moisture parameters from here if we aren't using the equivalent buoyancy formulation, and I don't think there are more parameters that could go here. We need no moisture parameters for thermal shallow water (usual formulation), and if we are doing moist thermal shallow water then the moisture parameters will be passed through with the physics scheme.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually am happy about keeping the parameters in here -- and maybe we should put other moist shallow water parameters in here too. We currently keep other moisture parameters in the parameters object for the compressible equations

Copy link
Contributor

@nhartney nhartney Nov 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, that's true. And I was wrong when I said that there are no other parameters that could go here - if we wanted to cover all our bases for moist shallow water options we'd need to add a beta1 parameter for convective feedback from moisture (to allow us to use the moist convective thermal and the moist convective pseudo-thermal equations). Then there's also the thresholds for phase changes and the proportions converted, but I feel that they don't belong with the equations though they are moist shallow water parameters.

# formulation of the thermal shallow water equations
beta2 = None
# Scaling factor for the saturation function in the equivalent buoyancy
# formulation of the thermal shallow water equations
nu = None
# Scaling factor for the saturation function in the equivalent buoyancy
# formulation of the thermal shallow water equations
q0 = None


class WrapperOptions(Configuration, metaclass=ABCMeta):
Expand Down
2 changes: 1 addition & 1 deletion gusto/equations/prognostic_equations.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __init__(self, field_names, domain, space_names,
None.
active_tracers (list, optional): a list of `ActiveTracer` objects
that encode the metadata for any active tracers to be included
in the equations.. Defaults to None.
in the equations. Defaults to None.
"""

self.field_names = field_names
Expand Down
Loading
Loading