Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
6b4b4a4
added initial implementation of physics options for the TR_bDF2 times…
Witt-D Sep 26, 2025
93d9204
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Sep 30, 2025
ffbcddc
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Sep 30, 2025
c448825
configuring physics
Witt-D Sep 30, 2025
198a3f5
Resolve merge
Witt-D Sep 30, 2025
9a4cf2f
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Oct 1, 2025
ef24c0f
fixed middle physics and added optional timestep scaling to the schemes
Witt-D Oct 2, 2025
7b3d72d
comparison for merge
Witt-D Oct 2, 2025
e490bd5
merge resolved
Witt-D Oct 2, 2025
07b49df
ensured that timestep multipluers were carried through correctly
Witt-D Oct 8, 2025
77a7605
added script to test physics
Witt-D Oct 9, 2025
c957687
testing script
Witt-D Oct 13, 2025
315d354
picked up fix
Witt-D Oct 13, 2025
2fbac59
case B implemented
Witt-D Oct 14, 2025
ea1889c
slow physics added, first attempt
Witt-D Oct 30, 2025
0f349d5
updated TR-BDF2 physics
Witt-D Nov 4, 2025
c4d56be
tr-bdf2 with debugging lines
Witt-D Nov 5, 2025
2d7622b
tr-bdf slow physics cases
Witt-D Nov 10, 2025
9245cb9
updated time discretisations to handle pased dt scales
Witt-D Nov 10, 2025
df77cd4
resolved tr-bdf2 merge
Witt-D Nov 10, 2025
454e5a8
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Dec 9, 2025
eb1c86a
added Xi physics options
Witt-D Dec 9, 2025
4d23e30
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Jan 5, 2026
fee93b2
different time dicretisation evaluationion
Witt-D Jan 6, 2026
8127c88
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Jan 6, 2026
ff3c697
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Jan 6, 2026
0d8898b
fast physics case F
Witt-D Jan 6, 2026
b3f15c3
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Jan 6, 2026
1aa1613
added case G fast schemes
Witt-D Jan 6, 2026
6aa6b23
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Jan 6, 2026
33a982b
some typos
Witt-D Jan 6, 2026
e1a799b
this is typo central
Witt-D Jan 6, 2026
0d40b09
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Jan 6, 2026
b605899
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Jan 6, 2026
caec86e
changed some debugging statements
Witt-D Jan 16, 2026
5d13d31
resolved merge
Witt-D Jan 16, 2026
b298906
saving state
Witt-D Jan 19, 2026
5f7a5f4
added improved slow options
Witt-D Jan 27, 2026
bc3b790
added fast physics
Witt-D Feb 19, 2026
9d146f1
removed debugging print statements
Witt-D Feb 19, 2026
535bd84
added TR predictor
Witt-D Feb 25, 2026
c4c4947
gmres solver
Witt-D Mar 2, 2026
c9befec
cleaned up physics options
Witt-D Mar 11, 2026
4ff8b6e
cleaned up physics
Witt-D Mar 12, 2026
478f838
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Mar 19, 2026
f84626f
initial tr-bdf2 model
Witt-D Mar 19, 2026
0d86db5
Merge branch 'DWitt/tr-bdf2-physics' of https://github.com/firedrakep…
Witt-D Mar 19, 2026
1ab1f5f
added TR-BDF2 model + flake8
Witt-D Mar 25, 2026
10c2f81
removed testing script which was not supposeed to be here
Witt-D Mar 25, 2026
e022624
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Mar 27, 2026
c1c7f45
added time scaling
Witt-D Jul 9, 2026
f117a8c
Merge branch 'main' of https://github.com/firedrakeproject/gusto into…
Witt-D Jul 9, 2026
bf1a8bc
pick up scaling changes2
Witt-D Jul 9, 2026
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
2 changes: 1 addition & 1 deletion gusto/diagnostics/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def setup(self, domain, state_fields, space=None):
if not hasattr(domain.spaces, space.name):
domain.spaces.add_space(space.name, space)

self.field = state_fields(self.name, space=space, dump=self.to_dump, pick_up=False)
self.field = state_fields(self.name, space=space, dump=self.to_dump, pick_up=True)

if self.method != 'solve':
assert self.expr is not None, \
Expand Down
215 changes: 214 additions & 1 deletion gusto/model/model.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from abc import ABCMeta, abstractmethod, abstractproperty
from firedrake import sqrt
from gusto.core import (Domain, IO, EmbeddedDGOptions)
from gusto.recovery import BoundaryMethod, RecoverySpaces
from gusto.spatial_methods import DGUpwind, InteriorPenaltyDiffusion
from gusto.time_discretisation import (SSPRK3, RungeKuttaFormulation,
BackwardEuler)
from gusto.timestepping import SemiImplicitQuasiNewton
from gusto.timestepping import SemiImplicitQuasiNewton, TRBDF2QuasiNewton


class ModelBase(object, metaclass=ABCMeta):
Expand Down Expand Up @@ -97,6 +98,218 @@ def run(self, t, tmax, pick_up=False):
self.stepper.run(t=t, tmax=tmax, pick_up=pick_up)


class TRBDF2ModelBase(ModelBase):
"""
Base for model classes using SIQN. Child classes should define the
standard transport and diffusion schemes and methods.
"""

@abstractproperty
def diffusion_methods(self):
"""
Needs to return a list of instances of the diffusion discretisation
class for each of the prognostic fields in the model equation set that
has diffusion options defined.
"""
pass

@abstractproperty
def diffusion_schemes(self):
"""
Need to return a list of instances of the timestepping scheme
class to be used for each of the prognostic fields in the model
equation set that has diffusion options defined.
"""
pass

@abstractproperty
def transported_fields(self):
"""
Needs to return a list of instances of the timestepping scheme class
to be used for each of the prognostic fields in the model equation set.
"""
pass

@abstractproperty
def transport_methods(self):
"""
Needs to return a list of instances of the transport method class
for each of the prognostic fields in the model equation set.
"""
pass

@property
def tr_tau_values(self):
_tau = {}
for field_name in self.equation.field_names:
if field_name != "u":
_tau[field_name] = 2 * self.gamma
return _tau

@property
def bdf_tau_values(self):
_tau = {}
for field_name in self.equation.field_names:
if field_name != "u":
_tau[field_name] = 1.0
return _tau

def setup(self, output, **kwargs):
"""
Args:
output (:class:`OutputParameters`): provides parameters
controlling output

Kwargs:
diagnostic_fields (list, optional): an iterable of `DiagnosticField`
objects. Defaults to None.
subcycling_options (:class:`SubcyclingOptions`, optional): an
object containing options for subcycling the time
discretisation. Defaults to None.

Remaining kwargs are passed straight through to the stepper - see
there for documentation.
"""
self.gamma = 1 - sqrt(2) / 2
diagnostic_fields = kwargs.pop("diagnostic_fields", None)
io = IO(self.domain, output, diagnostic_fields=diagnostic_fields)

self.subcycling_options = kwargs.pop("subcycling_options", None)
self.limiters = kwargs.pop("limiters", {})
self.stepper = TRBDF2QuasiNewton(
self.equation, io, self.transported_fields,
spatial_methods=self.transport_methods+self.diffusion_methods,
diffusion_schemes=self.diffusion_schemes,
tau_values_tr=self.tr_tau_values,
tau_values_bdf=self.bdf_tau_values,
**kwargs
)


class TRBDF2Model(TRBDF2ModelBase):
"""
TRBDF2 model class encapsulating the best settings for next-to-lowest order
methods.
"""
def __init__(self, mesh, dt, parameters, equations,
family=None, **kwargs):
"""
Args:
mesh (:class:`Mesh`): the model's mesh.
dt (float): the model timestep.
parameters (:class:`EquationParameters`): class storing the model
equation parameters.
equation (:class:`PrognosticEquationSet`): defines the model's
prognostic equation
family (str, optional): the finite element space family used for
the velocity field. This determines the other finite element
spaces used via the de Rham complex. If not provided, an
appropriate choice will be made based on the cell of the mesh
(or the base mesh in 3D). Defaults to None.

Kwargs:
Kwargs are passed straight through to the equation class; see
those classes for full documentation. Some common kwargs are
listed below.

u_transport_option (str, optional): specifies the transport term
used for the velocity equation. Supported options are:
'vector_invariant_form', 'vector_advection_form', and
'circulation_form'. Defaults to 'vector_invariant_form'.
diffusion_options (iterable, optional): iterable of
``(field_name, diffusion_parameters)`` pairs where
diffusion_parameters is a :class:`DiffusionParameters`
object specifying the diffusion parameters to be applied
to the field field_name. Defaults to None.
no_normal_flow_bc_ids (list, optional): a list of IDs of domain
boundaries at which no normal flow will be enforced. Defaults to
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.
"""
super().__init__(mesh, dt, parameters, equations, family=family,
element_order=1, **kwargs)

@property
def diffusion_methods(self):
"""
Returns a list of instances of the InteriorPenaltyDiffusion
class for each of the prognostic fields in the model equation set that
has diffusion options defined.
"""
_diffusion_methods = []
for field, params in self.diffusion_options:
_diffusion_methods.append(
InteriorPenaltyDiffusion(self.equation, field, params)
)
return _diffusion_methods

@property
def diffusion_schemes(self):
"""
Returns a list of instances of the Backward Euler timestepping scheme
class for each of the prognostic fields in the model equation set that
has diffusion options defined.
"""
_diffusion_schemes = []
for field, _ in self.diffusion_options:
_diffusion_schemes.append(
BackwardEuler(self.domain, field)
)
return _diffusion_schemes

@property
def transported_fields(self):
"""
Returns a list of instances of the SSPRK3 timestepping scheme class
for each of the prognostic fields in the model equation set.
"""
_transported_fields = []
for field_name in self.equation.field_names:
if self.equation.space_names[field_name] == 'L2':
_transported_fields.append(
SSPRK3(self.domain, field_name,
subcycling_options=self.subcycling_options,
rk_formulation=RungeKuttaFormulation.linear,
limiter=self.limiters.get(field_name))
)
elif self.equation.space_names[field_name] == 'theta':
_transported_fields.append(
SSPRK3(self.domain, field_name,
subcycling_options=self.subcycling_options,
options=EmbeddedDGOptions(),
limiter=self.limiters.get(field_name))
)
else:
_transported_fields.append(
SSPRK3(
self.domain, field_name,
subcycling_options=self.subcycling_options,
limiter=self.limiters.get(field_name))
)
return _transported_fields

@property
def transport_methods(self):
"""
Returns a list of instances of the DGUpwind transport method class
for each of the prognostic fields in the model equation set.
"""
_transport_methods = []
for field_name in self.equation.field_names:
if self.equation.space_names[field_name] == 'L2':
_transport_methods.append(
DGUpwind(self.equation, field_name,
advective_then_flux=True)
)
else:
_transport_methods.append(
DGUpwind(self.equation, field_name)
)
return _transport_methods


class SIQNModelBase(ModelBase):
"""
Base for model classes using SIQN. Child classes should define the
Expand Down
8 changes: 4 additions & 4 deletions gusto/time_discretisation/explicit_runge_kutta.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def __init__(self, domain, butcher_matrix, field_name=None,
self.nStages = int(np.shape(self.butcher_matrix)[0])
self.rk_formulation = rk_formulation

def setup(self, equation, apply_bcs=True, *active_labels):
def setup(self, equation, apply_bcs=True, *active_labels, dt_scale=None,):
"""
Set up the time discretisation based on the equation.

Expand All @@ -135,7 +135,7 @@ def setup(self, equation, apply_bcs=True, *active_labels):
*active_labels (:class:`Label`): labels indicating which terms of
the equation to include.
"""
super().setup(equation, apply_bcs, *active_labels)
super().setup(equation, apply_bcs, *active_labels, dt_scale=dt_scale)

if self.rk_formulation == RungeKuttaFormulation.predictor:
self.field_i = [Function(self.fs) for _ in range(self.nStages+1)]
Expand Down Expand Up @@ -448,7 +448,6 @@ def apply_cycle(self, x_out, x_in):
self.limiter.apply(x_in)

self.x1.assign(x_in)

for i in range(self.nStages):
self.solve_stage(x_in, i)
x_out.assign(self.x1)
Expand Down Expand Up @@ -501,7 +500,8 @@ def __init__(
rk_formulation=rk_formulation,
solver_parameters=solver_parameters,
limiter=limiter, options=options,
augmentation=augmentation)
augmentation=augmentation
)


class SSPRK2(ExplicitRungeKutta):
Expand Down
4 changes: 2 additions & 2 deletions gusto/time_discretisation/implicit_runge_kutta.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def __init__(self, domain, butcher_matrix, field_name=None,
self.nStages = int(np.shape(self.butcher_matrix)[1])
self.rk_formulation = rk_formulation

def setup(self, equation, apply_bcs=True, *active_labels):
def setup(self, equation, apply_bcs=True, *active_labels, dt_scale=None):
"""
Set up the time discretisation based on the equation.

Expand All @@ -100,7 +100,7 @@ def setup(self, equation, apply_bcs=True, *active_labels):
the equation to include.
"""

super().setup(equation, apply_bcs, *active_labels)
super().setup(equation, apply_bcs, *active_labels, dt_scale=dt_scale)

self.k = [Function(self.fs) for i in range(self.nStages)]

Expand Down
26 changes: 16 additions & 10 deletions gusto/time_discretisation/time_discretisation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
u"""
"""
Objects for discretising time derivatives.

Time discretisation objects discretise ∂y/∂t = F(y), for variable y, time t and
Expand All @@ -9,9 +9,9 @@
from functools import cached_property
import math

from firedrake import (Function, TestFunction, TestFunctions, DirichletBC,

Check failure on line 12 in gusto/time_discretisation/time_discretisation.py

View workflow job for this annotation

GitHub Actions / Run linter

F401

gusto/time_discretisation/time_discretisation.py:12:1: F401 'firedrake.dot' imported but unused
NonlinearVariationalProblem, NonlinearVariationalSolver,
FunctionSpace)
FunctionSpace, dot)
from firedrake.fml import (replace_subject, replace_test_function, Term,
all_terms, drop)
from firedrake.formmanipulation import split_form
Expand Down Expand Up @@ -91,17 +91,17 @@
self.domain = domain
self.field_name = field_name
self.equation = None
R = FunctionSpace(domain.mesh, "R", 0)
self.dt = Function(R, val=0.0)
self.R = FunctionSpace(domain.mesh, "R", 0)
self.dt = Function(self.R, val=0.0)
self.dt.assign(domain.dt)
self.original_dt = Function(R, val=0.0)
self.original_dt = Function(self.R, val=0.0)
self.original_dt.assign(self.dt)
self.options = options
self.limiter = limiter
self.courant_max = None
self.augmentation = augmentation
self.subcycling_options = subcycling_options

Check failure on line 104 in gusto/time_discretisation/time_discretisation.py

View workflow job for this annotation

GitHub Actions / Run linter

W293

gusto/time_discretisation/time_discretisation.py:104:1: W293 blank line contains whitespace
if self.subcycling_options is not None:
self.subcycling_options.check_options()

Expand Down Expand Up @@ -144,7 +144,7 @@
else:
self.solver_parameters = solver_parameters

def setup(self, equation, apply_bcs=True, *active_labels):
def setup(self, equation, apply_bcs=True, *active_labels, dt_scale=None,):
"""
Set up the time discretisation based on the equation.

Expand All @@ -158,6 +158,11 @@
self.equation = equation
self.residual = equation.residual

if dt_scale is not None:
dt_scale_func = Function(self.R).assign(dt_scale)
self.dt.assign(self.dt * dt_scale_func)
self.original_dt.assign(self.original_dt * dt_scale_func)

if self.field_name is not None and hasattr(equation, "field_names"):
if isinstance(self.field_name, list):
# Multiple fields are being solved for simultaneously.
Expand Down Expand Up @@ -494,9 +499,10 @@
subcycling_options=subcycling_options,
solver_parameters=solver_parameters,
limiter=limiter, options=options,
augmentation=augmentation)
augmentation=augmentation
)

def setup(self, equation, apply_bcs=True, *active_labels):
def setup(self, equation, apply_bcs=True, *active_labels, dt_scale=None):

Check failure on line 505 in gusto/time_discretisation/time_discretisation.py

View workflow job for this annotation

GitHub Actions / Run linter

E241

gusto/time_discretisation/time_discretisation.py:505:46: E241 multiple spaces after ','
"""
Set up the time discretisation based on the equation.

Expand All @@ -507,7 +513,7 @@
*active_labels (:class:`Label`): labels indicating which terms of
the equation to include.
"""
super().setup(equation, apply_bcs, *active_labels)
super().setup(equation, apply_bcs, *active_labels, dt_scale=dt_scale)

# get default solver options if none passed in
self.solver_parameters.update(mass_parameters(
Expand Down
Loading
Loading