Skip to content

Fix the hydrostatic switch #584

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

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
aa641dd
allow option for hydrostatic switch with non-hydrostatic gravity wave…
tommbendall Dec 8, 2024
533ce02
add schaer mountain, update gravity wave plot and implement spin up
tommbendall Dec 8, 2024
8c4c376
attempt to fix hydrostatic switch with fml
tommbendall Dec 8, 2024
75e0435
implement more changes for the hydrostatic switch
tommbendall Dec 9, 2024
e0fd5e7
update initial condition figure for schaer mountain
tommbendall Dec 9, 2024
cd02bc6
Merge branch 'TBendall/HydrostaticTests' into TBendall/HydrostaticSwitch
tommbendall Dec 9, 2024
9eb6ceb
settings for schaer mountain to improve stability
tommbendall Dec 18, 2024
fd73141
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticT…
tommbendall Dec 18, 2024
43c277e
improve stability settings for schaer mountain
tommbendall Dec 18, 2024
684e5b1
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticT…
tommbendall Dec 18, 2024
b2f5b3c
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticT…
tommbendall Dec 18, 2024
7eabbfb
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticT…
tommbendall Dec 18, 2024
3c7fd3c
move hydrostatic params to file, neaten up spinup period and fix Scha…
tommbendall Dec 19, 2024
8275ce8
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticT…
tommbendall Dec 19, 2024
3569478
Merge branch 'TBendall/HydrostaticTests' into TBendall/HydrostaticSwitch
tommbendall Dec 19, 2024
4cb089c
it doesn't seem to be working now, so start a new branch ...
tommbendall Jan 3, 2025
2e39786
Merge remote-tracking branch 'origin/main' into TBendall/HydrostaticS…
tommbendall Mar 21, 2025
1f7bff4
fix conflicts
tommbendall Apr 2, 2025
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
31 changes: 22 additions & 9 deletions examples/compressible_euler/skamarock_klemp_nonhydrostatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
logger, SUPGOptions, Perturbation, CompressibleParameters,
CompressibleEulerEquations, HydrostaticCompressibleEulerEquations,
compressible_hydrostatic_balance, RungeKuttaFormulation, CompressibleSolver,
SubcyclingOptions, hydrostatic_parameters
SubcyclingOptions, LinearVariationalProblem, LinearVariationalSolver,
TestFunction, TrialFunction, dx
)

skamarock_klemp_nonhydrostatic_defaults = {
Expand Down Expand Up @@ -67,6 +68,8 @@ def skamarock_klemp_nonhydrostatic(
# ------------------------------------------------------------------------ #

element_order = 1
alpha = 0.5
u_eqn_type = 'vector_advection_form'

# ------------------------------------------------------------------------ #
# Set up model objects
Expand All @@ -78,9 +81,11 @@ def skamarock_klemp_nonhydrostatic(
domain = Domain(mesh, dt, "CG", element_order)

# Equation
parameters = CompressibleParameters(mesh)
parameters = CompressibleParameters()
if hydrostatic:
eqns = HydrostaticCompressibleEulerEquations(domain, parameters)
eqns = HydrostaticCompressibleEulerEquations(
domain, parameters, u_transport_option=u_eqn_type
)
else:
eqns = CompressibleEulerEquations(domain, parameters)

Expand Down Expand Up @@ -135,17 +140,14 @@ def skamarock_klemp_nonhydrostatic(

# Linear solver
if hydrostatic:
linear_solver = CompressibleSolver(
eqns, solver_parameters=hydrostatic_parameters,
overwrite_solver_parameters=True
)
linear_solver = CompressibleSolver(eqns, alpha=alpha)
else:
linear_solver = CompressibleSolver(eqns)

# Time stepper
stepper = SemiImplicitQuasiNewton(
eqns, io, transported_fields, transport_methods,
linear_solver=linear_solver
linear_solver=linear_solver, alpha=alpha, num_outer=2, num_inner=2
)

# ------------------------------------------------------------------------ #
Expand Down Expand Up @@ -175,12 +177,23 @@ def skamarock_klemp_nonhydrostatic(
# Calculate hydrostatic exner
compressible_hydrostatic_balance(eqns, theta_b, rho_b)

# Define initial theta
theta_pert = (
deltaTheta * sin(pi*z/domain_height)
/ (1 + (x - domain_width/2)**2 / pert_width**2)
)
theta0.interpolate(theta_b + theta_pert)
rho0.assign(rho_b)

# find perturbed rho
gamma = TestFunction(Vr)
rho_trial = TrialFunction(Vr)
dx_qp = dx(degree=domain.max_quad_degree)
lhs = gamma * rho_trial * dx_qp
rhs = gamma * (rho_b * theta_b / theta0) * dx_qp
rho_problem = LinearVariationalProblem(lhs, rhs, rho0)
rho_solver = LinearVariationalSolver(rho_problem)
rho_solver.solve()

u0.project(as_vector([wind_initial, 0.0]))

stepper.set_reference_profiles([('rho', rho_b), ('theta', theta_b)])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,6 @@ def test_skamarock_klemp_nonhydrostatic_parallel():
test_skamarock_klemp_nonhydrostatic()


# Hydrostatic equations not currently working
@pytest.mark.xfail
def test_hyd_switch_skamarock_klemp_nonhydrostatic():
from skamarock_klemp_nonhydrostatic import skamarock_klemp_nonhydrostatic
test_name = 'hyd_switch_skamarock_klemp_nonhydrostatic'
Expand All @@ -81,8 +79,6 @@ def test_hyd_switch_skamarock_klemp_nonhydrostatic():
)


# Hydrostatic equations not currently working
@pytest.mark.xfail
@pytest.mark.parallel(nprocs=2)
def test_hyd_switch_skamarock_klemp_nonhydrostatic_parallel():
test_hyd_switch_skamarock_klemp_nonhydrostatic()
Expand Down
2 changes: 2 additions & 0 deletions gusto/core/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,5 @@ def __call__(self, target, value=None):
hydrostatic = DynamicsLabel("hydrostatic")
incompressible = DynamicsLabel("incompressible")
sponge = DynamicsLabel("sponge")
horizontal_prognostic = Label("horizontal_prognostic")
vertical_prognostic = Label("vertical_prognostic")
92 changes: 53 additions & 39 deletions gusto/equations/compressible_euler_equations.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
sin, pi, inner, dx, div, cross, FunctionSpace, FacetNormal, jump, avg, dS_v,
conditional, SpatialCoordinate, split, Constant, as_vector
)
from firedrake.fml import subject, replace_subject
from firedrake.fml import subject, drop, keep
from gusto.core.labels import (
time_derivative, transport, prognostic, hydrostatic, linearisation,
pressure_gradient, coriolis, gravity, sponge
pressure_gradient, coriolis, gravity, sponge, implicit,
horizontal_prognostic
)
from gusto.equations.thermodynamics import exner_pressure
from gusto.equations.common_forms import (
Expand Down Expand Up @@ -335,49 +336,62 @@ def __init__(self, domain, parameters, sponge_options=None,
no_normal_flow_bc_ids=no_normal_flow_bc_ids,
active_tracers=active_tracers)

# Replace
self.residual = self.residual.label_map(
lambda t: t.has_label(time_derivative),
map_if_true=lambda t: self.hydrostatic_projection(t, 'u')
)

# Add an extra hydrostatic term
u_idx = self.field_names.index('u')
u = split(self.X)[u_idx]
w = self.tests[u_idx]
k = self.domain.k
self.residual += hydrostatic(
subject(
prognostic(
-inner(k, self.tests[u_idx]) * inner(k, u) * dx, "u"),
self.X
)
u_vert = k*inner(k, u)
u_hori = u - u_vert

# -------------------------------------------------------------------- #
# Add hydrostatic term
# -------------------------------------------------------------------- #
# Term to appear in both explicit and implicit forcings
# For the explicit forcing, this will cancel out the u^n part of the
# time derivative
self.residual += hydrostatic(subject(prognostic(
inner(k, w)*inner(k, u)/domain.dt*dx, 'u'), self.X)
)

def hydrostatic_projection(self, term, field_name):
"""
Performs the 'hydrostatic' projection.
# Term that appears only in implicit forcing
# For the implicit forcing, in combination with the term above, the
# u^{n+1} term will be cancelled out
self.residual += implicit(hydrostatic(subject(prognostic(
Constant(-1.0)*inner(k, w)*inner(k, u)/domain.dt*dx, 'u'), self.X))
)

Takes a term involving a vector prognostic variable and replaces the
prognostic with only its horizontal components. It also adds the
'hydrostatic' label to that term.
# # Add Euler-Poincare term
# self.residual += hydrostatic(subject(prognostic(
# Constant(0.5)*div(w)*inner(u_hori, u)*dx, 'u'), self.X)
# )

Args:
term (:class:`Term`): the term to perform the projection upon.
field_name (str): the prognostic field to make hydrostatic.
# -------------------------------------------------------------------- #
# Only transport horizontal wind
# -------------------------------------------------------------------- #
# Drop wind transport term, it needs replacing with a version that
# includes only the horizontal components for the transported field
self.residual = self.residual.label_map(
lambda t: t.has_label(transport) and t.get(prognostic) == 'u',
map_if_true=drop,
map_if_false=keep
)

Returns:
:class:`LabelledForm`: the labelled form containing the new term.
"""
# u_term = prognostic(
# advection_equation_circulation_form(domain, w, u_hori, u), 'u'
# )

f_idx = self.field_names.index(field_name)
k = self.domain.k
X = term.get(subject)
field = split(X)[f_idx]

new_subj = field - inner(field, k) * k
# In one step:
# - set up the replace_subject routine (which returns a function)
# - call that function on the supplied `term` argument,
# to replace the subject with the new hydrostatic subject
# - add the hydrostatic label
return replace_subject(new_subj, old_idx=f_idx)(term)
# Velocity transport term -- depends on formulation
if u_transport_option == "vector_invariant_form":
u_term = prognostic(vector_invariant_form(domain, w, u_hori, u), 'u')
elif u_transport_option == "vector_advection_form":
u_term = prognostic(advection_form(w, u_hori, u), 'u')
elif u_transport_option == "circulation_form":
circ_form = prognostic(
advection_equation_circulation_form(domain, w, u_hori, u), 'u'
)
ke_form = prognostic(kinetic_energy_form(w, u_hori, u), 'u')
u_term = ke_form + circ_form
else:
raise ValueError("Invalid u_transport_option: %s" % u_transport_option)

self.residual += horizontal_prognostic(subject(u_term, self.X))
Loading