"""Objects describe chemical conversion and reaction processes."""
from firedrake import dx, split, Function
from firedrake.fml import subject
from gusto.core.labels import prognostic
from gusto.core.logging import logger
from gusto.physics.physics_parametrisation import PhysicsParametrisation
__all__ = ["TerminatorToy"]
[docs]
class TerminatorToy(PhysicsParametrisation):
"""
Setup the Terminator Toy chemistry interaction
as specified in 'The terminator toy chemistry test ...'
Lauritzen et. al. (2014).
The coupled equations for the two species are given by:
D/Dt (X) = 2Kx
D/Dt (X2) = -Kx
where Kx = k1*X2 - k2*(X**2)
"""
def __init__(self, equation, k1=1, k2=1,
species1_name='X', species2_name='X2'):
"""
Args:
equation (:class: 'PrognosticEquationSet'): the model's equation
k1(float, optional): Reaction rate for species 1 (X). Defaults to a
constant 1 over the domain.
k2(float, optional): Reaction rate for species 2 (X2). Defaults to a
constant 1 over the domain.
species1_name(str, optional): Name of the first interacting species.
Defaults to 'X'.
species2_name(str, optional): Name of the second interacting
species. Defaults to 'X2'.
"""
label_name = 'terminator_toy'
super().__init__(equation, label_name, parameters=None)
if species1_name not in equation.field_names:
raise ValueError(f"Field {species1_name} does not exist in the equation set")
if species2_name not in equation.field_names:
raise ValueError(f"Field {species2_name} does not exist in the equation set")
self.species1_idx = equation.field_names.index(species1_name)
self.species2_idx = equation.field_names.index(species2_name)
assert equation.function_space.sub(self.species1_idx) == equation.function_space.sub(self.species2_idx), \
"The function spaces for the two species need to be the same"
self.Xq = Function(equation.X.function_space())
Xq = self.Xq
species1 = split(Xq)[self.species1_idx]
species2 = split(Xq)[self.species2_idx]
test_1 = equation.tests[self.species1_idx]
test_2 = equation.tests[self.species2_idx]
Kx = k1*species2 - k2*(species1**2)
source1_expr = test_1 * 2*Kx * dx
source2_expr = test_2 * -Kx * dx
equation.residual -= self.label(subject(prognostic(source1_expr, 'X'), Xq), self.evaluate)
equation.residual -= self.label(subject(prognostic(source2_expr, 'X2'), Xq), self.evaluate)
[docs]
def evaluate(self, x_in, dt):
"""
Evaluates the source/sink for the coalescence process.
Args:
x_in (:class:`Function`): the (mixed) field to be evolved.
dt (:class:`Constant`): the time interval for the scheme.
"""
logger.info(f'Evaluating physics parametrisation {self.label.label}')
pass