Source code for thetis.implicitexplicit
"""
Implicit-explicit time integrators
"""
from .rungekutta import *
[docs]
class IMEXGeneric(TimeIntegrator, ABC):
    """
    Generic implementation of Runge-Kutta Implicit-Explicit schemes
    Derived classes must define the implicit :attr:`dirk_class` and explicit
    :attr:`erk_class` Runge-Kutta time integrator classes.
    This method solves the linearized equations: All implicit terms are fed to
    the implicit solver, while all the other terms are fed to the explicit
    solver. In case of non-linear terms proper linearization must defined in the
    equation using the two solution functions (solution, solution_old)
    """
    @abstractproperty
    def dirk_class(self):
        """Implicit DIRK class"""
        pass
    @abstractproperty
    def erk_class(self):
        """Explicit Runge-Kutta class"""
        pass
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.__init__")
    def __init__(self, equation, solution, fields, dt, options, bnd_conditions):
        """
        :arg equation: equation to solve
        :type equation: :class:`Equation` object
        :arg solution: :class:`Function` where solution will be stored
        :arg fields: Dictionary of fields that are passed to the equation
        :type fields: dict of :class:`Function` or :class:`Constant` objects
        :arg float dt: time step in seconds
        :arg options: :class:`TimeStepperOptions` instance containing parameter values.
        :arg dict bnd_conditions: Dictionary of boundary conditions passed to the equation
        """
        super(IMEXGeneric, self).__init__(equation, solution, fields, dt, options)
        # NOTE: The same solver parameters are currently used for both implicit and explicit schemes
        # implicit scheme
        self.dirk = self.dirk_class(equation, solution, fields, dt, options,
                                    bnd_conditions=bnd_conditions,
                                    terms_to_add=('implicit'))
        self.dirk.ad_block_tag += '_impl'
        # explicit scheme
        self.erk = self.erk_class(equation, solution, fields, dt, options,
                                  bnd_conditions=bnd_conditions,
                                  terms_to_add=('explicit', 'source'))
        self.erk.ad_block_tag += '_expl'
        assert self.erk.n_stages == self.dirk.n_stages
        self.n_stages = self.erk.n_stages
        # FIXME this assumes that we are limited by whatever ERK solves ...
        # FIXME may violate max DIRK SSP time step (if any)
        self.cfl_coeff = self.erk.cfl_coeff
[docs]
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.update_solver")
    def update_solver(self):
        """Create solver objects"""
        self.dirk.update_solver()
        self.erk.update_solver() 
[docs]
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.initialize")
    def initialize(self, solution):
        """Assigns initial conditions to all required fields."""
        self.dirk.initialize(solution)
        self.erk.initialize(solution) 
[docs]
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.advance")
    def advance(self, t, update_forcings=None):
        """Advances equations for one time step."""
        for i in range(self.n_stages):
            self.solve_stage(i, t, update_forcings)
        self.get_final_solution() 
[docs]
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.solve_stage")
    def solve_stage(self, i_stage, t, update_forcings=None):
        """
        Solves i-th stage
        """
        # set solution to u_n + dt*sum(a*k_erk)
        self.erk.update_solution(i_stage, additive=False)
        # set correct reference solution in the DIRK solver
        self.dirk.solution_old.assign(self.erk.solution)
        # solve implicit tendency (this is implicit solve)
        self.dirk.solve_tendency(i_stage, t, update_forcings)
        # set solution to u_n + dt*sum(a*k_erk) + *sum(a*k_dirk)
        self.dirk.update_solution(i_stage)
        # evaluate explicit tendency
        self.erk.solve_tendency(i_stage, t, update_forcings) 
[docs]
    @PETSc.Log.EventDecorator("thetis.IMEXGeneric.get_final_solution")
    def get_final_solution(self):
        """
        Evaluates the final solution.
        """
        # set solution to u_n + sum(b*k_erk)
        self.erk.get_final_solution(additive=False)
        # set correct reference solution in the DIRK solver
        self.dirk.solution_old.assign(self.erk.solution)
        # set solution to u_n + sum(b*k_erk) + sum(b*k_dirk)
        self.dirk.get_final_solution()
        # update old solution
        self.erk.solution_old.assign(self.dirk.solution) 
[docs]
    def set_dt(self, dt):
        """
        Update time step
        :arg float dt: time step
        """
        self.erk.set_dt(dt)
        self.dirk.set_dt(dt) 
 
[docs]
class IMEXLPUM2(IMEXGeneric):
    """
    SSP-IMEX RK scheme (20) in Higureras et al. (2014)
    CFL coefficient is 2.0
    Higueras et al (2014). Optimized strong stability preserving IMEX
    Runge-Kutta methods. Journal of Computational and Applied Mathematics
    272(2014) 116-140. http://dx.doi.org/10.1016/j.cam.2014.05.011
    """
    erk_class = ERKLPUM2
    dirk_class = DIRKLPUM2 
[docs]
class IMEXLSPUM2(IMEXGeneric):
    """
    SSP-IMEX RK scheme (17) in Higureras et al. (2014)
    CFL coefficient is 2.0
    Higueras et al (2014). Optimized strong stability preserving IMEX
    Runge-Kutta methods. Journal of Computational and Applied Mathematics
    272(2014) 116-140. http://dx.doi.org/10.1016/j.cam.2014.05.011
    """
    erk_class = ERKLSPUM2
    dirk_class = DIRKLSPUM2 
[docs]
class IMEXMidpoint(IMEXGeneric):
    """
    Implicit-explicit midpoint scheme (1, 2, 2) from Ascher et al. (1997)
    Ascher et al. (1997). Implicit-explicit Runge-Kutta methods for
    time-dependent partial differential equations. Applied Numerical
    Mathematics, 25:151-167. http://dx.doi.org/10.1137/0732037
    """
    erk_class = ERKMidpoint
    dirk_class = ESDIRKMidpoint 
[docs]
class IMEXEuler(IMEXGeneric):
    """
    Forward-Backward Euler
    """
    erk_class = ERKEuler
    dirk_class = BackwardEuler