diff --git a/examples/compressible/dcmip_3_1_meanflow_quads.py b/examples/compressible/dcmip_3_1_meanflow_quads.py index 82611ef04..c96bbe9ff 100644 --- a/examples/compressible/dcmip_3_1_meanflow_quads.py +++ b/examples/compressible/dcmip_3_1_meanflow_quads.py @@ -84,7 +84,7 @@ io = IO(domain, output, diagnostic_fields=diagnostic_fields) # Transport schemes -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho", subcycles=2), SSPRK3(domain, "theta", options=SUPGOptions(), subcycles=2)] transport_methods = [DGUpwind(eqns, field) for field in ["u", "rho", "theta"]] diff --git a/examples/compressible/moist_bryan_fritsch.py b/examples/compressible/moist_bryan_fritsch.py index 41f9930a9..c00191dad 100644 --- a/examples/compressible/moist_bryan_fritsch.py +++ b/examples/compressible/moist_bryan_fritsch.py @@ -62,7 +62,7 @@ SSPRK3(domain, "theta", options=EmbeddedDGOptions()), SSPRK3(domain, "water_vapour", options=EmbeddedDGOptions()), SSPRK3(domain, "cloud_water", options=EmbeddedDGOptions()), - ImplicitMidpoint(domain, "u")] + TrapeziumRule(domain, "u")] transport_methods = [DGUpwind(eqns, field) for field in ["u", "rho", "theta", "water_vapour", "cloud_water"]] diff --git a/examples/compressible/mountain_hydrostatic.py b/examples/compressible/mountain_hydrostatic.py index a57b2c530..b0bd8a76f 100644 --- a/examples/compressible/mountain_hydrostatic.py +++ b/examples/compressible/mountain_hydrostatic.py @@ -71,7 +71,7 @@ # Transport schemes theta_opts = SUPGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] transport_methods = [DGUpwind(eqns, "u"), diff --git a/examples/compressible/mountain_nonhydrostatic.py b/examples/compressible/mountain_nonhydrostatic.py index 890f95ba2..47ad15d2f 100644 --- a/examples/compressible/mountain_nonhydrostatic.py +++ b/examples/compressible/mountain_nonhydrostatic.py @@ -70,7 +70,7 @@ # Transport schemes theta_opts = SUPGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] transport_methods = [DGUpwind(eqns, "u"), diff --git a/examples/compressible/robert_bubble.py b/examples/compressible/robert_bubble.py index b89f6aa33..3e771f60e 100644 --- a/examples/compressible/robert_bubble.py +++ b/examples/compressible/robert_bubble.py @@ -52,7 +52,7 @@ # Transport schemes theta_opts = EmbeddedDGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] diff --git a/examples/compressible/skamarock_klemp_hydrostatic.py b/examples/compressible/skamarock_klemp_hydrostatic.py index bfdaf4107..42d111f1e 100644 --- a/examples/compressible/skamarock_klemp_hydrostatic.py +++ b/examples/compressible/skamarock_klemp_hydrostatic.py @@ -56,7 +56,7 @@ # Transport schemes theta_opts = SUPGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] diff --git a/examples/compressible/skamarock_klemp_nonlinear.py b/examples/compressible/skamarock_klemp_nonlinear.py index e84c01d64..2b544e043 100644 --- a/examples/compressible/skamarock_klemp_nonlinear.py +++ b/examples/compressible/skamarock_klemp_nonlinear.py @@ -64,7 +64,7 @@ # Transport schemes theta_opts = SUPGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] transport_methods = [DGUpwind(eqns, "u"), diff --git a/examples/compressible/straka_bubble.py b/examples/compressible/straka_bubble.py index cbe32800d..20442bf44 100644 --- a/examples/compressible/straka_bubble.py +++ b/examples/compressible/straka_bubble.py @@ -62,7 +62,7 @@ # Transport schemes theta_opts = SUPGOptions() - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=theta_opts)] transport_methods = [DGUpwind(eqns, "u"), diff --git a/examples/incompressible/skamarock_klemp_incompressible.py b/examples/incompressible/skamarock_klemp_incompressible.py index 26efb5f52..ce38a5230 100644 --- a/examples/incompressible/skamarock_klemp_incompressible.py +++ b/examples/incompressible/skamarock_klemp_incompressible.py @@ -54,7 +54,7 @@ # Transport schemes b_opts = SUPGOptions() -transported_fields = [ImplicitMidpoint(domain, "u"), +transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "b", options=b_opts)] transport_methods = [DGUpwind(eqns, "u"), DGUpwind(eqns, "b", ibp=b_opts.ibp)] diff --git a/examples/shallow_water/williamson_2.py b/examples/shallow_water/williamson_2.py index 1c2a8f8ec..432dd7cc6 100644 --- a/examples/shallow_water/williamson_2.py +++ b/examples/shallow_water/williamson_2.py @@ -66,7 +66,7 @@ io = IO(domain, output, diagnostic_fields=diagnostic_fields) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "D", subcycles=2)] transport_methods = [DGUpwind(eqns, "u"), DGUpwind(eqns, "D")] diff --git a/examples/shallow_water/williamson_5.py b/examples/shallow_water/williamson_5.py index bb6b5d9b3..0bcb43a3d 100644 --- a/examples/shallow_water/williamson_5.py +++ b/examples/shallow_water/williamson_5.py @@ -70,7 +70,7 @@ io = IO(domain, output, diagnostic_fields=diagnostic_fields) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "D")] transport_methods = [DGUpwind(eqns, "u"), DGUpwind(eqns, "D")] diff --git a/gusto/time_discretisation.py b/gusto/time_discretisation.py index a4db0a168..f535dddee 100644 --- a/gusto/time_discretisation.py +++ b/gusto/time_discretisation.py @@ -18,7 +18,7 @@ __all__ = ["ForwardEuler", "BackwardEuler", "SSPRK3", "RK4", "Heun", - "ThetaMethod", "ImplicitMidpoint", "BDF2", "TR_BDF2", "Leapfrog", "AdamsMoulton", "AdamsBashforth"] + "ThetaMethod", "TrapeziumRule", "BDF2", "TR_BDF2", "Leapfrog", "AdamsMoulton", "AdamsBashforth"] def wrapper_apply(original_apply): @@ -634,23 +634,24 @@ def apply(self, x_out, x_in): class ThetaMethod(TimeDiscretisation): """ - Implements the theta implicit-explicit timestepping method. + Implements the theta implicit-explicit timestepping method, which can + be thought as a generalised trapezium rule. The theta implicit-explicit timestepping method for operator F is written as y^(n+1) = y^n + dt*(1-theta)*F[y^n] + dt*theta*F[y^(n+1)] for off-centring parameter theta. """ - def __init__(self, domain, field_name=None, theta=None, + def __init__(self, domain, theta, field_name=None, solver_parameters=None, options=None): """ Args: domain (:class:`Domain`): the model's domain object, containing the mesh and the compatible function spaces. + theta (float): the off-centring parameter. theta = 1 + corresponds to a backward Euler method. Defaults to None. field_name (str, optional): name of the field to be evolved. Defaults to None. - theta (float, optional): the off-centring parameter. theta = 1 - corresponds to a backward Euler method. Defaults to None. solver_parameters (dict, optional): dictionary of parameters to pass to the underlying solver. Defaults to None. options (:class:`AdvectionOptions`, optional): an object containing @@ -661,9 +662,7 @@ def __init__(self, domain, field_name=None, theta=None, Raises: ValueError: if theta is not provided. """ - # TODO: would this be better as a non-optional argument? Or should the - # check be on the provided value? - if theta is None: + if (theta < 0 or theta > 1): raise ValueError("please provide a value for theta between 0 and 1") if isinstance(options, (EmbeddedDGOptions, RecoveryOptions)): raise NotImplementedError("Only SUPG advection options have been implemented for this time discretisation") @@ -715,11 +714,12 @@ def apply(self, x_out, x_in): x_out.assign(self.x_out) -class ImplicitMidpoint(ThetaMethod): +class TrapeziumRule(ThetaMethod): """ - Implements the implicit midpoint timestepping method. + Implements the trapezium rule timestepping method, also commonly known as + Crank Nicholson. - The implicit midpoint timestepping method for operator F is written as + The trapezium rule timestepping method for operator F is written as y^(n+1) = y^n + dt/2*F[y^n] + dt/2*F[y^(n+1)]. It is equivalent to the "theta" method with theta = 1/2. """ @@ -739,7 +739,7 @@ def __init__(self, domain, field_name=None, solver_parameters=None, to control the "wrapper" methods, such as Embedded DG or a recovery method. Defaults to None. """ - super().__init__(domain, field_name, theta=0.5, + super().__init__(domain, 0.5, field_name, solver_parameters=solver_parameters, options=options) diff --git a/integration-tests/balance/test_compressible_balance.py b/integration-tests/balance/test_compressible_balance.py index 03ecc285b..a9726dd5a 100644 --- a/integration-tests/balance/test_compressible_balance.py +++ b/integration-tests/balance/test_compressible_balance.py @@ -39,7 +39,7 @@ def setup_balance(dirname): io = IO(domain, output) # Set up transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta", options=EmbeddedDGOptions())] transport_methods = [DGUpwind(eqns, 'u'), diff --git a/integration-tests/balance/test_saturated_balance.py b/integration-tests/balance/test_saturated_balance.py index 59b16fe3e..2c89da089 100644 --- a/integration-tests/balance/test_saturated_balance.py +++ b/integration-tests/balance/test_saturated_balance.py @@ -82,7 +82,7 @@ def setup_saturated(dirname, recovered): if recovered: transported_fields.append(SSPRK3(domain, 'u', options=u_opts)) else: - transported_fields.append(ImplicitMidpoint(domain, 'u')) + transported_fields.append(TrapeziumRule(domain, 'u')) transport_methods = [DGUpwind(eqns, 'u'), DGUpwind(eqns, 'rho'), diff --git a/integration-tests/balance/test_unsaturated_balance.py b/integration-tests/balance/test_unsaturated_balance.py index 6f29f8c9d..c6e3f33a9 100644 --- a/integration-tests/balance/test_unsaturated_balance.py +++ b/integration-tests/balance/test_unsaturated_balance.py @@ -77,7 +77,7 @@ def setup_unsaturated(dirname, recovered): if recovered: transported_fields.append(SSPRK3(domain, "u", options=u_opts)) else: - transported_fields.append(ImplicitMidpoint(domain, "u")) + transported_fields.append(TrapeziumRule(domain, "u")) transport_methods = [DGUpwind(eqns, 'u'), DGUpwind(eqns, 'rho'), diff --git a/integration-tests/equations/test_dry_compressible.py b/integration-tests/equations/test_dry_compressible.py index 53377335a..2e293e474 100644 --- a/integration-tests/equations/test_dry_compressible.py +++ b/integration-tests/equations/test_dry_compressible.py @@ -39,7 +39,7 @@ def run_dry_compressible(tmpdir): io = IO(domain, output) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta")] transport_methods = [DGUpwind(eqn, 'u'), diff --git a/integration-tests/equations/test_incompressible.py b/integration-tests/equations/test_incompressible.py index 12260d1c9..a3ac3d8d0 100644 --- a/integration-tests/equations/test_incompressible.py +++ b/integration-tests/equations/test_incompressible.py @@ -39,7 +39,7 @@ def run_incompressible(tmpdir): # Transport Schemes b_opts = SUPGOptions() - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "b", options=b_opts)] transport_methods = [DGUpwind(eqn, "u"), DGUpwind(eqn, "b", ibp=b_opts.ibp)] diff --git a/integration-tests/equations/test_moist_compressible.py b/integration-tests/equations/test_moist_compressible.py index 222914334..e65a27157 100644 --- a/integration-tests/equations/test_moist_compressible.py +++ b/integration-tests/equations/test_moist_compressible.py @@ -40,7 +40,7 @@ def run_moist_compressible(tmpdir): io = IO(domain, output) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "rho"), SSPRK3(domain, "theta")] transport_methods = [DGUpwind(eqn, "u"), diff --git a/integration-tests/equations/test_sw_fplane.py b/integration-tests/equations/test_sw_fplane.py index b508ca43d..305f7c81d 100644 --- a/integration-tests/equations/test_sw_fplane.py +++ b/integration-tests/equations/test_sw_fplane.py @@ -36,7 +36,7 @@ def run_sw_fplane(tmpdir): io = IO(domain, output, diagnostic_fields=[CourantNumber()]) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "D")] transport_methods = [DGUpwind(eqns, "u"), DGUpwind(eqns, "D")] diff --git a/integration-tests/equations/test_sw_triangle.py b/integration-tests/equations/test_sw_triangle.py index dd637e365..455595c31 100644 --- a/integration-tests/equations/test_sw_triangle.py +++ b/integration-tests/equations/test_sw_triangle.py @@ -156,7 +156,7 @@ def test_sw_setup(tmpdir, u_transport_option): domain, eqns, io = setup_sw(dirname, dt, u_transport_option) # Transport schemes - transported_fields = [ImplicitMidpoint(domain, "u"), + transported_fields = [TrapeziumRule(domain, "u"), SSPRK3(domain, "D")] transport_methods = [DGUpwind(eqns, 'u'), diff --git a/integration-tests/model/test_time_discretisation.py b/integration-tests/model/test_time_discretisation.py index 1b1f04106..5be78c871 100644 --- a/integration-tests/model/test_time_discretisation.py +++ b/integration-tests/model/test_time_discretisation.py @@ -28,7 +28,7 @@ def test_time_discretisation(tmpdir, scheme, tracer_setup): if scheme == "ssprk": transport_scheme = SSPRK3(domain) elif scheme == "implicit_midpoint": - transport_scheme = ImplicitMidpoint(domain) + transport_scheme = TrapeziumRule(domain) elif scheme == "RK4": transport_scheme = RK4(domain) elif scheme == "Heun": diff --git a/integration-tests/transport/test_supg_transport.py b/integration-tests/transport/test_supg_transport.py index 30422d8e6..813308ec7 100644 --- a/integration-tests/transport/test_supg_transport.py +++ b/integration-tests/transport/test_supg_transport.py @@ -39,7 +39,7 @@ def test_supg_transport_scalar(tmpdir, equation_form, scheme, space, if scheme == "ssprk": transport_scheme = SSPRK3(domain, options=opts) elif scheme == "implicit_midpoint": - transport_scheme = ImplicitMidpoint(domain, options=opts) + transport_scheme = TrapeziumRule(domain, options=opts) transport_method = DGUpwind(eqn, "f", ibp=ibp) timestepper = PrescribedTransport(eqn, transport_scheme, setup.io, transport_method) @@ -81,7 +81,7 @@ def test_supg_transport_vector(tmpdir, equation_form, scheme, space, if scheme == "ssprk": transport_scheme = SSPRK3(domain, options=opts) elif scheme == "implicit_midpoint": - transport_scheme = ImplicitMidpoint(domain, options=opts) + transport_scheme = TrapeziumRule(domain, options=opts) transport_method = DGUpwind(eqn, "f", ibp=ibp) timestepper = PrescribedTransport(eqn, transport_scheme, setup.io, transport_method)