def add_data(self, *args, **kwargs):
        """
        Parameters
        ----------
        solver : :py:class:`.IIterativeTimeSolver`
            solver

        state : :py:class:`.ISolverState`
            state of the solver

        Raises
        ------
        ValueError

            * if ``solver`` is not given or is not a :py:class:`.IIterativeTimeSolver`
            * if ``state`` is not given or is not a :py:class:`.ISolverState`
        """
        super(MultiSolutionAnalyzer, self).add_data(args, kwargs)

        assert_named_argument('solver', kwargs, types=IIterativeTimeSolver,
                              descriptor="Solver", checking_obj=self)

        assert_named_argument('state', kwargs, types=ISolverState, descriptor="State", checking_obj=self)

        self._solvers.append(kwargs['solver'])
        self._data.append(kwargs['state'])
    def link_solvers(self, *args, **kwargs):
        """Links the given communicators with this communicator

        Parameters
        ----------
        previous : :py:class:`.ForwardSendingMessaging`
            communicator of the previous solver
        next : :py:class:`.ForwardSendingMessaging`
            communicator of the next solver

        Raises
        ------
        ValueError
            if one of the two communicators of the specified type is not given
        """
        super(ForwardSendingMessaging, self).link_solvers(*args, **kwargs)
        assert_condition(len(kwargs) == 2,
                         ValueError, message="Exactly two communicators must be given: NOT %d" % len(kwargs),
                         checking_obj=self)

        assert_named_argument('previous', kwargs, types=ForwardSendingMessaging, descriptor="Previous Communicator",
                              checking_obj=self)
        self._previous = kwargs['previous']

        assert_named_argument('next', kwargs, types=ForwardSendingMessaging, descriptor="Next Communicator",
                              checking_obj=self)
        self._next = kwargs['next']
Beispiel #3
0
    def __init__(self, **kwargs):
        """
        Parameters
        ----------
        solution_class : :py:class:`.ISolution`, :py:class:`.StepSolutionData` or :py:class:`.TrajectorySolutionData`

        element_type : :py:class:`.IStepState` or :py:class:`.IStateItertor`

        num_states : :py:class:`int`
            *(optional)*

        Raises
        ------
        ValueError
            if ``num_states`` is not a non-zero positive integer
        """
        assert_named_argument('solution_class', kwargs, descriptor="Solution Type", checking_obj=self)
        assert_named_argument('element_type', kwargs, descriptor="Element Type", checking_obj=self)
        self._solution = kwargs['solution_class']()
        del kwargs['solution_class']
        self._element_type = kwargs['element_type']
        del kwargs['element_type']
        self._states = []
        self._current_index = 0
        self._finalized = False

        if 'num_states' in kwargs:
            _num_states = kwargs['num_states']
            assert_condition(isinstance(_num_states, int) and _num_states > 0,
                             ValueError, message="Number of states must be a non-zero positive integer: NOT {}"
                                                 .format(_num_states),
                             checking_obj=self)
            self._states = [self._element_type(**kwargs) for i in range(0, _num_states)]
    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        fine_nodes : :py:class:`numpy.ndarray` of :py:class:`float`
        coarse_nodes : :py:class:`numpy.ndarray` of :py:class:`float`
        """
        super(TimeTransitionProvider, self).__init__(*args, **kwargs)
        assert_named_argument('fine_nodes', kwargs, np.ndarray, descriptor='Fine Nodes', checking_obj=self)
        assert_condition(len(kwargs['fine_nodes'].shape) == 1, ValueError,
                         message="Fine Nodes must have a single dimension: NOT %s" % kwargs['fine_nodes'].shape,
                         checking_obj=self)

        assert_named_argument('coarse_nodes', kwargs, np.ndarray, descriptor='Coarse Nodes', checking_obj=self)
        assert_condition(len(kwargs['coarse_nodes'].shape) == 1, ValueError,
                         message="Coarse Nodes must have a single dimension: NOT %s" % kwargs['coarse_nodes'].shape,
                         checking_obj=self)

        assert_condition(kwargs['fine_nodes'].size >= kwargs['coarse_nodes'].size, ValueError,
                         message="There must be more or at least as many Fine Nodes than Coarse Nodes: NOT %d < %d"
                                 % (kwargs['fine_nodes'].size, kwargs['coarse_nodes'].size),
                         checking_obj=self)

        self._n_fine_points = kwargs['fine_nodes'].size
        self._fine_nodes = kwargs['fine_nodes']

        self._n_coarse_points = kwargs['coarse_nodes'].size
        self._coarse_nodes = kwargs['coarse_nodes']

        self._weight_scale = kwargs['weight_scale'] if 'weight_scale' in kwargs else 2

        self._compute_prolongation_matrix()
        self._compute_restringation_matrix()
Beispiel #5
0
    def __init__(self, **kwargs):
        """

        Parameters
        ----------
        num_states : :py:class:`int`
            number of states in this sequence

        solution_class : :py:class:`.TrajectorySolutionData` or :py:class:`.StepSolutionData`
            *(optional)*
            defaults to :py:class:`.TrajectorySolutionData`

        element_type : :py:class:`.IStepState` or :py:class:`.IStateIterator`
            *(optional)*
            defaults to :py:class:`.IStepState`

        Raises
        ------
        ValueError
            if ``num_states`` is not given
        """
        if 'solution_class' not in kwargs:
            kwargs['solution_class'] = TrajectorySolutionData
        if 'element_type' not in kwargs:
            kwargs['element_type'] = IStepState
        assert_named_argument('num_states', kwargs, types=int, descriptor="Number of States", checking_obj=self)
        super(ITimeStepState, self).__init__(**kwargs)

        self._delta_time_step = 0.0
        self._initial = IStepState()
 def evaluate_wrt_space(self, **kwargs):
     """
     Parameters
     ----------
     values : :py:class:`numpy.ndarray`
     """
     assert_named_argument('values', kwargs, types=np.ndarray, descriptor="Values", checking_obj=self)
     return self.get_rhs_space_operators('default')\
                 .dot(kwargs['values'].flatten())\
                 .reshape(kwargs['values'].shape)
Beispiel #7
0
 def __init__(self, **kwargs):
     """
     Parameters
     ----------
     communicator : :py:class:`.ICommunicationProvider`
     """
     assert_named_argument('communicator', kwargs, types=ICommunicationProvider, descriptor="Communicator",
                           checking_obj=self)
     self._communicator = kwargs['communicator']
     self._states = []
Beispiel #8
0
    def implicit_solve(self, next_x, func, method="direct", **kwargs):
        """Space-Solver for the Heat Equation

        Parameters
        ----------
        next_x : :py:class:`numpy.ndarray`
            initial value for the MG solver
        func :
            *unused*
        method : :py:class:`str`
            method specifying the space solver;
            one of ``mg`` or ``direct`` (default)
        time_level : :py:class:`int`
            time level in MLSDC-notation (i.e. 0 is base level of MLSDC)
        delta_time : :py:class:`float`
            distance from the previous to currently calculated time node
        """
        # this_got_called(self, next_x=next_x, func=func, **kwargs)
        assert_named_argument('expl_term', kwargs, types=np.ndarray, descriptor="RHS for Space Solver",
                              checking_obj=self)
        # assert_named_argument('time_level', kwargs, types=int, descriptor="Time Level", checking_obj=self)
        if kwargs.get('time_level') is None:
            kwargs['time_level'] = 0
        time_level = str(kwargs['time_level'])
        assert_named_argument('delta_time', kwargs, types=float, descriptor="Delta Time Node", checking_obj=self)
        delta_time = str(kwargs['delta_time'])
        # assert_is_key(time_level, self._direct_solvers, key_desc="Time Level", dict_desc="Direct Solvers",
        #               checking_obj=self)
        # assert_is_key(delta_time, self._direct_solvers[time_level],
        #               key_desc="Delta Time '%s'" % delta_time,
        #               dict_desc="Direct Solvers for Time Level '%s'" % 'time_level', checking_obj=self)
        if time_level not in self._direct_solvers or delta_time not in self._direct_solvers[time_level]:
            self.initialize_direct_space_solver(kwargs['time_level'], kwargs['delta_time'])
        _this_set = self._direct_solvers[time_level][delta_time]
        _this_set['mg_level'].rhs = kwargs['expl_term']
        # LOG.debug("initial RHS: %s" % _this_set['mg_level'].rhs)
        _this_set['stencil'].modify_rhs(_this_set['mg_level'])
        # LOG.debug("modified RHS: %s" % _this_set['mg_level'].rhs)
        # LOG.debug("Stencil: %s" % _this_set['stencil'].arr)

        _sol = self.mg_solve(next_x,
                             rhs=_this_set['mg_level'].rhs,
                             method=self._implicit_solve_method,
                             solver=_this_set['solver'],
                             stencil_fnc=lambda level: self.mg_stencil(kwargs['delta_time'], level.h))

        # LOG.debug("Implicit Solve => %s" % _sol)
        _this_set['mg_level'].mid[:] = _sol.reshape(-1)
        # _padded_sol = _this_set['mg_level'].evaluable_view(_this_set['stencil'])
        _sp_matrix = _this_set['stencil'].to_sparse_matrix(self.spacial_dim)
        # LOG.debug("Check with Sparse Matrix %s:" % _sp_matrix.todense())
        # LOG.debug("  ==> %s" % _sp_matrix.dot(_sol.reshape(-1)).reshape(_sol.shape))
        return _sol
Beispiel #9
0
    def run(self, state, **kwargs):
        """Implicit Euler step method.

        .. math::

            u_{m+1}^{k+1} - \\Delta_\\tau F(t_{m+1}, u_{m+1}^{k+1}) =
                u_m^{k+1} + \\Delta_\\tau F(t_{m+1}, u_{m+1}^k) + \\Delta_t I_m^{m+1} \\left( F(\\vec{u}^k) \\right)

        Parameters
        ----------
        solver_state : :py:class:`.SdcSolverState`
        """
        super(ImplicitSdcCore, self).run(state, **kwargs)

        assert_is_instance(state, SdcSolverState, descriptor="State", checking_obj=self)
        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)
        _problem = kwargs['problem']

        _previous_iteration_current_step = self._previous_iteration_current_step(state)

        if problem_has_direct_implicit(_problem, self):
            _previous_iteration_previous_step = self._previous_iteration_previous_step(state)

            _sol = _problem.direct_implicit(phis_of_time=[_previous_iteration_previous_step.value,
                                                          _previous_iteration_current_step.value,
                                                          state.current_time_step.previous_step.value],
                                            delta_node=state.current_step.delta_tau,
                                            integral=state.current_step.integral,
                                            core=self)
        else:
            # using step-wise formula
            #   u_{m+1}^{k+1} - \Delta_\tau F(u_{m+1}^{k+1})
            #     = u_m^{k+1} - \Delta_\tau F(u_m^k) + \Delta_t I_m^{m+1}(F(u^k))
            # Note: \Delta_t is always 1.0 as it's part of the integral
            _expl_term = \
                (state.current_time_step.previous_step.value
                 - state.current_step.delta_tau
                 * _problem.evaluate_wrt_time(state.current_step.time_point,
                                              _previous_iteration_current_step.value)
                 + state.current_step.integral).reshape(-1)
            _func = lambda x_next: \
                _expl_term \
                + state.current_step.delta_tau \
                  * _problem.evaluate_wrt_time(state.current_step.time_point,
                                               x_next.reshape(_problem.dim_for_time_solver)).reshape(-1) \
                - x_next
            _sol = _problem.implicit_solve(state.current_step.value.reshape(-1), _func)

        if type(state.current_step.value) == type(_sol):
            state.current_step.value = _sol
        else:
            state.current_step.value = _sol[0]
 def evaluate_wrt_space(self, **kwargs):
     """
     Parameters
     ----------
     values : :py:class:`numpy.ndarray`
     """
     assert_named_argument("values", kwargs, types=np.ndarray, descriptor="Values", checking_obj=self)
     assert_named_argument("delta_time", kwargs, types=float, descriptor="Delta Time Node", checking_obj=self)
     return (
         self.get_rhs_space_operators(kwargs["delta_time"])
         .dot(kwargs["values"].flatten())
         .reshape(kwargs["values"].shape)
     )
Beispiel #11
0
    def run(self, state, **kwargs):
        """Explicit Euler step method.

        .. math::

            u_{m+1}^{k+1} = u_m^{k+1} + \\Delta_\\tau \\left( F(t_m, u_m^{k+1}) - F(t_m, u_m^k) \\right)
                                      + \\Delta_t I_m^{m+1} \\left( F(\\vec{u}^k) \\right)

        Parameters
        ----------
        solver_state : :py:class:`.MlSdcSolverState`
        """
        super(ExplicitMlSdcCore, self).run(state, **kwargs)

        assert_is_instance(state, MlSdcSolverState, descriptor="State", checking_obj=self)
        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)

        _problem = kwargs['problem']

        _previous_step = state.previous_step

        if not state.current_iteration.on_finest_level:
            LOG.debug("taking restringated or coarse-corrected value")
            if state.current_step != state.current_level.first:
                _previous_iteration_previous_step = state.current_iteration.current_level.previous_step.initial
            else:
                _previous_iteration_previous_step = state.current_iteration.current_level.initial
        else:
            _previous_iteration_previous_step = self._previous_iteration_previous_step(state)

        if not _previous_step.rhs_evaluated:
            _previous_step.rhs = _problem.evaluate_wrt_time(state.current_step.time_point, _previous_step.value)
        if not _previous_iteration_previous_step.rhs_evaluated:
            _previous_iteration_previous_step.rhs = \
                _problem.evaluate_wrt_time(state.current_step.time_point, _previous_iteration_previous_step.value)

        _fas = np.zeros(_previous_step.rhs.shape, dtype=_previous_step.rhs.dtype)
        if _previous_step.has_fas_correction():
            # LOG.debug("   previous step has FAS")
            _fas = _previous_step.fas_correction

        # Note: \Delta_t is always 1.0 as it's part of the integral
        # using step-wise formula
        # Formula:
        #   u_{m+1}^{k+1} = u_m^{k+1} + \Delta_\tau [ F(u_m^{k+1}) - F(u_m^k) ] + \Delta_t I_m^{m+1}(F(u^k))
        state.current_step.value = \
            (_previous_step.value
             + state.current_step.delta_tau * (_previous_step.rhs - _previous_iteration_previous_step.rhs)
             + state.current_step.integral + _fas)
    def add_data(self, *args, **kwargs):
        """
        Parameters
        ----------
        state : :py:class:`.ISolverState`
            state of the solver

        Raises
        ------
        ValueError
            if ``state`` is not given or is not a :py:class:`.ISolverState`
        """
        super(SingleSolutionAnalyzer, self).add_data(args, kwargs)
        assert_named_argument('state', kwargs, types=ISolverState, descriptor="State", checking_obj=self)
        self._data = kwargs['state']
Beispiel #13
0
    def compute_error(self, state, **kwargs):
        super(SdcSolverCore, self).compute_error(state, **kwargs)

        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)

        _problem = kwargs['problem']

        if problem_has_exact_solution(_problem, self):
            # LOG.debug("Error for t={:.3f}: {} - {}".format(state.current_step.time_point,
            #                                               state.current_step.value,
            #                                               _problem.exact(state.current_step.time_point)))
            state.current_step.solution.error = Error(
                abs(state.current_step.value - _problem.exact(state.current_step.time_point))
            )
        else:
            # we need the exact solution for that
            #  (unless we find an error approximation method)
            pass
Beispiel #14
0
    def init(self, problem, **kwargs):
        """Initializes MLSDC solver with given problem, integrator and multi-level provider.

        Parameters
        ----------
        ml_provider : :py:class:`.MultiLevelProvider`
            *(required)*
            handler for the different levels to use
        num_nodes : :py:class:`int`
            *(otional)*
            number of nodes per time step
        nodes_type : :py:class:`.INodes`
            *(optional)*
            Type of integration nodes to be used (class name, **NOT instance**).
        weights_type : :py:class:`.IWeightFunction`
            *(optional)*
            Integration weights function to be used (class name, **NOT instance**).

        Raises
        ------
        ValueError :

            * if given problem is not an :py:class:`.IInitialValueProblem`
            * if number of nodes per time step is not given; neither through ``num_nodes``, ``nodes_type`` nor
              ``integrator``
            * if no :py:class:`.MultiLevelProvider` is given

        See Also
        --------
        :py:meth:`.IIterativeTimeSolver.init`
            overridden method (with further parameters)
        :py:meth:`.IParallelSolver.init`
            mixed in overridden method (with further parameters)
        """
        assert_is_instance(problem, IInitialValueProblem, descriptor="Initial Value Problem", checking_obj=self)

        assert_named_argument('ml_provider', kwargs, types=MultiTimeLevelProvider,
                              descriptor='Multi Time Level Provider', checking_obj=self)
        self._ml_provider = kwargs['ml_provider']

        super(MlSdc, self).init(problem, **kwargs)

        # TODO: need to store the exact solution somewhere else
        self.__exact = np.zeros(self.ml_provider.integrator(-1).num_nodes, dtype=np.object)
    def run(self, **kwargs):
        """
        Parameters
        ----------
        solver : :py:class:`.IIterativeTimeSolver`

        Raises
        ------
        ValueError
            if ``solver`` is not given and not an :py:class:`.IIterativeTimeSolver`
        """
        super(SingleSolutionAnalyzer, self).run(**kwargs)

        assert_named_argument('solver', kwargs, types=IIterativeTimeSolver, descriptor="Solver", checking_obj=self)

        # plot the last solution
        self._plotter.plot(solver=kwargs['solver'],
                           state=self._data,
                           errorplot=True,
                           residualplot=True)
Beispiel #16
0
    def __init__(self, **kwargs):
        """

        Parameters
        ----------
        num_time_steps : :py:class:`int`
            number of time steps in this sequence

        num_states : :py:class:`int`
            number of steps per time step

        solution_class : :py:class:`.TrajectorySolutionData`, *any other solution class*
            *(optional)*
            defaults to :py:class:`.TrajectorySolutionData`

        element_type : :py:class:`.IStateIterator`
            *(optional)*
            defaults to :py:class:`.ITimeStepState`

        Raises
        ------
        ValueError
            if ``num_time_steps`` is not given
        """
        if 'solution_class' not in kwargs:
            kwargs['solution_class'] = TrajectorySolutionData
        if 'element_type' not in kwargs:
            kwargs['element_type'] = ITimeStepState
        super(IIterationState, self).__init__(**kwargs)
        del kwargs['solution_class']
        del kwargs['element_type']

        assert_named_argument('num_time_steps', kwargs, types=int, descriptor="Number of Time Steps", checking_obj=self)
        _num_time_steps = kwargs['num_time_steps']
        del kwargs['num_time_steps']
        self._states = [self._element_type(**kwargs) for i in range(0, _num_time_steps)]

        self._delta_interval = 0.0
        self._initial = None
Beispiel #17
0
    def direct_implicit(self, *args, **kwargs):
        """Direct Implicit Formula for :math:`u'(t, \\phi_t) &= \\lambda u(t, \\phi_t)`
        """
        assert_named_argument('phis_of_time', kwargs, checking_obj=self)
        assert_named_argument('delta_node', kwargs, checking_obj=self)
        assert_named_argument('integral', kwargs, checking_obj=self)

        _phis = kwargs['phis_of_time']
        assert_is_instance(_phis, list, message="Direct implicit formula needs multiple phis.", checking_obj=self)
        assert_condition(len(_phis) == 3, ValueError, message="Need exactly three different phis.", checking_obj=self)
        for _phi in _phis:
            assert_condition(_phi.shape == self.dim_for_time_solver, ValueError,
                             message="Given phi is of wrong shape: %s != %s" % (_phi.shape, self.dim_for_time_solver),
                             checking_obj=self)

        # _phis[0] : previous iteration -> previous step
        # _phis[1] : previous iteration -> current step
        # _phis[2] : current iteration -> previous step

        _dn = kwargs['delta_node']
        # TODO: make this numerics check more advanced (better warning for critical numerics)
        if isinstance(self.lmbda, complex):
            assert_condition(_dn * self.lmbda.real != 1.0,
                             ArithmeticError, "Direct implicit formula for lambda={:f} and dn={:f} not valid. "
                             .format(self.lmbda, _dn) + "Try implicit solver.",
                             self)
        else:
            assert_condition(_dn * self.lmbda != 1.0,
                             ArithmeticError, "Direct implicit formula for lambda={:f} and dn={:f} not valid. "
                             .format(self.lmbda, _dn) + "Try implicit solver.",
                             self)

        _int = kwargs['integral']

        _fas = kwargs['fas'] \
            if 'fas' in kwargs and kwargs['fas'] is not None else 0.0

        if 'core' in kwargs \
                and (isinstance(kwargs['core'], (ImplicitSdcCore, ImplicitMlSdcCore))
                     or (isinstance(self.lmbda, complex) and isinstance(kwargs['core'], SemiImplicitMlSdcCore))):
            _new = (_phis[2] - _dn * self.lmbda * _phis[1] + _int + _fas) / (1 - self.lmbda * _dn)
            # LOG.debug("Implicit MLSDC Step:\n  %s = (%s - %s * %s * %s + %s + %s) / (1 - %s * %s)"
            #           % (_new, _phis[2], _dn, self.lmbda, _phis[1], _int, _fas, self.lmbda, _dn))
            return _new
        else:
            _new = \
                (_phis[2]
                 + _dn * (complex(0, self.lmbda.imag) * (_phis[2] - _phis[0]) - self.lmbda.real * _phis[1])
                 + _int + _fas) \
                / (1 - self.lmbda.real * _dn)
            # LOG.debug("Semi-Implicit MLSDC Step:\n  %s = (%s + %s * (%s * (%s - %s) - %s * %s) + %s + %s) / (1 - %s * %s)"
            #           % (_new, _phis[2],  _dn, complex(0, self.lmbda.imag), _phis[2], _phis[0], self.lmbda.real, _phis[1], _int, _fas, self.lmbda.real, _dn))
            return _new
    def direct_implicit(self, *args, **kwargs):
        """Direct Implicit Formula for :math:`u'(t, \\phi_t) &= \\lambda u(t, \\phi_t)`
        """
        assert_is_instance(self.lmbda, complex,
                           message="Direct implicit formula only valid for imaginay lambda: NOT %s"
                                   % class_name(self.lmbda),
                           checking_obj=self)

        assert_named_argument('phis_of_time', kwargs, checking_obj=self)
        assert_named_argument('delta_node', kwargs, checking_obj=self)
        assert_named_argument('integral', kwargs, checking_obj=self)

        _phis = kwargs["phis_of_time"]
        assert_is_instance(_phis, list, message="Direct implicit formula needs multiple phis.", checking_obj=self)
        assert_condition(len(_phis) == 3, ValueError, message="Need exactly three different phis.", checking_obj=self)

        # _phis[0] : previous iteration -> previous step
        # _phis[1] : previous iteration -> current step
        # _phis[2] : current iteration -> previous step

        _dn = kwargs["delta_node"]
        # TODO: make this numerics check more advanced (better warning for critical numerics)
        assert_condition(_dn * self.lmbda.real != 1.0,
                         ArithmeticError, "Direct implicit formula for lambda={:f} and dn={:f} not valid. "
                                          .format(self.lmbda, _dn) + "Try implicit solver.",
                         self)
        _int = kwargs["integral"]

        if 'core' in kwargs and isinstance(kwargs['core'], ImplicitSdcCore):
            return (_phis[2] - _dn * self.lmbda * _phis[1] + _int) / (1 - self.lmbda * _dn)
        else:
            return \
                (_phis[2]
                 + _dn * (complex(0, self.lmbda.imag) * (_phis[2] - _phis[0]) - self.lmbda.real * _phis[1])
                 + _int) \
                / (1 - self.lmbda.real * _dn)
Beispiel #19
0
    def run(self, state, **kwargs):
        """Implicit Euler step method.

        .. math::

            u_{m+1}^{k+1} - \\Delta_\\tau F(t_{m+1}, u_{m+1}^{k+1}) =
                u_m^{k+1} + \\Delta_\\tau F(t_{m+1}, u_{m+1}^k) + \\Delta_t I_m^{m+1} \\left( F(\\vec{u}^k) \\right)

        Parameters
        ----------
        solver_state : :py:class:`.MlSdcSolverState`
        """
        super(ImplicitMlSdcCore, self).run(state, **kwargs)

        assert_is_instance(state, MlSdcSolverState, descriptor="State", checking_obj=self)
        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)
        _problem = kwargs['problem']

        use_intermediate = kwargs['use_intermediate'] if 'use_intermediate' in kwargs else False

        if use_intermediate:
            # LOG.debug("using intermediate")
            _previous_iteration_current_step = state.current_iteration.current_level.current_step.intermediate
        elif not state.current_iteration.on_finest_level:
            _previous_iteration_current_step = state.current_iteration.current_level.current_step
        else:
            _previous_iteration_current_step = self._previous_iteration_current_step(state)
        if not _previous_iteration_current_step.rhs_evaluated:
            _previous_iteration_current_step.rhs = \
                _problem.evaluate_wrt_time(_previous_iteration_current_step.time_point,
                                           _previous_iteration_current_step.value)

        if not state.current_iteration.on_finest_level:
            _previous_iteration_previous_step = state.current_iteration.current_level.previous_step
        else:
            _previous_iteration_previous_step = self._previous_iteration_previous_step(state)
        if not _previous_iteration_previous_step.rhs_evaluated:
            _previous_iteration_previous_step.rhs = \
                _problem.evaluate_wrt_time(_previous_iteration_previous_step.time_point,
                                           _previous_iteration_previous_step.value)

        _fas = np.zeros(_previous_iteration_current_step.rhs.shape,
                        dtype=_previous_iteration_current_step.rhs.dtype)
        if not use_intermediate and _previous_iteration_current_step.has_fas_correction():
            # LOG.debug("   previous iteration current step has FAS: %s"
            #           % _previous_iteration_current_step.fas_correction)
            _fas = _previous_iteration_current_step.fas_correction

        if problem_has_direct_implicit(_problem, self):
            if not state.current_iteration.on_finest_level:
                _previous_iteration_previous_step = state.current_iteration.current_level.previous_step
            else:
                _previous_iteration_previous_step = self._previous_iteration_previous_step(state)

            _sol = _problem.direct_implicit(phis_of_time=[_previous_iteration_previous_step.value,
                                                          _previous_iteration_current_step.value,
                                                          state.previous_step.value],
                                            delta_node=state.current_step.delta_tau,
                                            integral=state.current_step.integral,
                                            fas=_fas,
                                            core=self)
        else:
            # using step-wise formula
            #   u_{m+1}^{k+1} - \Delta_\tau F(u_{m+1}^{k+1})
            #     = u_m^{k+1} - \Delta_\tau F(u_m^k) + \Delta_t I_m^{m+1}(F(u^k))
            # Note: \Delta_t is always 1.0 as it's part of the integral
            _expl_term = \
                state.current_time_step.previous_step.value \
                - state.current_step.delta_tau * _previous_iteration_current_step.rhs \
                + state.current_step.integral + _fas
            _func = lambda x_next: \
                _expl_term \
                + state.current_step.delta_tau * _problem.evaluate_wrt_time(state.current_step.time_point, x_next) \
                - x_next
            _sol = _problem.implicit_solve(state.current_step.value, _func)

        if type(state.current_step.value) == type(_sol):
            state.current_step.value = _sol
        else:
            state.current_step.value = _sol[0]
Beispiel #20
0
    def run(self, core, **kwargs):
        """Applies SDC solver to the initialized problem setup.

        Solves the given problem with the explicit SDC algorithm.

        Parameters
        ----------
        core : :py:class:`.SdcSolverCore`
            core solver stepping method
        dt : :py:class:`float`
            width of the interval to work on; this is devided into the number of given
            time steps this solver has been initialized with

        See Also
        --------
        :py:meth:`.IIterativeTimeSolver.run` : overridden method
        """
        super(MlSdc, self).run(core, **kwargs)

        assert_named_argument('dt', kwargs, types=float, descriptor="Width of Interval", checking_obj=self)
        self._dt = kwargs['dt']

        self._print_header()

        # start iterations
        # TODO: exact solution storage handling
        self.__exact[0] = self.problem.initial_value

        _has_work = True
        _previous_flag = Message.SolverFlag.none
        _current_flag = Message.SolverFlag.none
        __work_loop_count = 1

        while _has_work:
            # LOG.debug("Work Loop: %d" % __work_loop_count)
            _previous_flag = _current_flag
            _current_flag = Message.SolverFlag.none

            # receive dedicated message
            _msg = self._communicator.receive(tag=(self.ml_provider.num_levels - 1))

            if _msg.flag == Message.SolverFlag.failed:
                # previous solver failed
                # --> pass on the failure and abort
                _current_flag = Message.SolverFlag.failed
                _has_work = False
                # LOG.debug("Previous Solver Failed")
            else:
                if _msg.flag == Message.SolverFlag.time_adjusted:
                    # the previous solver has adjusted its interval
                    # --> we need to recompute our interval
                    _current_flag = self._adjust_interval_width()
                    # we don't immediately start the computation of the newly computed interval
                    # but try to pass the new interval end to the next solver as soon as possible
                    # (this should avoid throwing away useless computation)
                    # LOG.debug("Previous Solver Adjusted Time")
                else:
                    if _previous_flag in \
                            [Message.SolverFlag.none, Message.SolverFlag.converged, Message.SolverFlag.finished,
                             Message.SolverFlag.time_adjusted]:
                        # we just started or finished our previous interval
                        # --> start a new interval
                        _has_work = self._init_new_interval(_msg.time_point)

                        if _has_work:
                            # set initial values
                            self.state.initial.value = _msg.value.copy()
                            self.state.initial.solution.time_point = _msg.time_point
                            self.state.initial.done()

                            # LOG.debug("New Interval Initialized")

                            # start logging output
                            self._print_interval_header()

                            # start global timing (per interval)
                            self.timer.start()
                        else:
                            # LOG.debug("No New Interval Available")
                            pass
                    elif _previous_flag == Message.SolverFlag.iterating:
                        # LOG.debug("Next Iteration")
                        pass
                    else:
                        # LOG.warn("WARNING!!! Something went wrong here")
                        pass

                    if _has_work:
                        # we are still on the same interval or have just successfully initialized a new interval
                        # --> do the real computation
                        # LOG.debug("Starting New Solver Main Loop")

                        # initialize a new iteration state
                        self.state.proceed()

                        if _msg.time_point == self.state.initial.time_point:
                            if _previous_flag == Message.SolverFlag.iterating:
                                # LOG.debug("Updating initial value")
                                # if the previous solver has a new initial value for us, we use it
                                self.state.current_iteration.initial.value = _msg.value.copy()

                        _current_flag = self._main_solver_loop()

                        if _current_flag in \
                                [Message.SolverFlag.converged, Message.SolverFlag.finished, Message.SolverFlag.failed]:
                            _log_msgs = {'': OrderedDict()}
                            if self.state.last_iteration_index <= self.threshold.max_iterations:
                                _group = 'Converged after %d iteration(s)' % (self.state.last_iteration_index + 1)
                                _log_msgs[''][_group] = OrderedDict()
                                _log_msgs[''][_group] = self.threshold.has_reached(log=True)
                                _log_msgs[''][_group]['Final Residual'] = "{:.3e}"\
                                    .format(supremum_norm(self.state.last_iteration.final_step.solution.residual))
                                _log_msgs[''][_group]['Solution Reduction'] = "{:.3e}"\
                                    .format(supremum_norm(self.state.solution
                                                          .solution_reduction(self.state.last_iteration_index)))
                                if problem_has_exact_solution(self.problem, self):
                                    _log_msgs[''][_group]['Error Reduction'] = "{:.3e}"\
                                        .format(supremum_norm(self.state.solution
                                                              .error_reduction(self.state.last_iteration_index)))
                            else:
                                warnings.warn("{}: Did not converged: {:s}".format(self._core.name, self.problem))
                                _group = "FAILED: After maximum of {:d} iteration(s)"\
                                         .format(self.state.last_iteration_index + 1)
                                _log_msgs[''][_group] = OrderedDict()
                                _log_msgs[''][_group]['Final Residual'] = "{:.3e}"\
                                    .format(supremum_norm(self.state.last_iteration.final_step.solution.residual))
                                _log_msgs[''][_group]['Solution Reduction'] = "{:.3e}"\
                                    .format(supremum_norm(self.state.solution
                                                          .solution_reduction(self.state.last_iteration_index)))
                                if problem_has_exact_solution(self.problem, self):
                                    _log_msgs[''][_group]['Error Reduction'] = "{:.3e}"\
                                        .format(supremum_norm(self.state.solution
                                                              .error_reduction(self.state.last_iteration_index)))
                                LOG.warn("  {} Failed: Maximum number iterations reached without convergence."
                                         .format(self._core.name))
                            print_logging_message_tree(_log_msgs)
                    elif _previous_flag in [Message.SolverFlag.converged, Message.SolverFlag.finished]:
                        # LOG.debug("Solver Finished.")

                        self.timer.stop()

                        self._print_footer()
                    else:
                        # something went wrong
                        # --> we failed
                        # LOG.warn("Solver failed.")
                        _current_flag = Message.SolverFlag.failed

            self._communicator.send(value=self.state.current_iteration.finest_level.final_step.value,
                                    time_point=self.state.current_iteration.finest_level.final_step.time_point,
                                    flag=_current_flag)
            __work_loop_count += 1

        # end while:has_work is None
        # LOG.debug("Solver Main Loop Done")

        return [_s.solution for _s in self._states]
    def mg_solve(self, next_x, method='direct', **kwargs):
        """Runs the multigrid solver

        This is where all the magic happens on each call of the space solver from the iterative time solver, i.e. on
        every iteration for each time-level in each sweep on each step.

        Parameters
        ----------
        method : :py:class:`str`
            defaults to ``direct``

            ``mg``
                for full multigrid cycles; additional keyword arguments passed to the multigrid solver can be given;
                additional arguments required:

                    ``mg_level``

            ``direct``
                for using the a predefined multigrid smoother as a direct solver via :py:class:`.DirectSolverSmoother`;
                additional arguments required:

                    ``mg_level``

                    ``stencil``

        Raises
        ------
        ValueError
            if given ``method`` is not one of ``mg`` or ``direct``

        Returns
        -------
        solution
        """
        if method == 'mg':
            assert_named_argument('stencil_fnc', kwargs, descriptor="Stencil Generation Function",
                                  checking_obj=self)
            assert_is_callable(kwargs['stencil_fnc'], descriptor="Stencil Generation Function", checking_obj=self)
            # LOG.debug("Using Multigrid as implicit space solver.")

            mg_core_options = {}
            mg_core_options.update(MG_SMOOTHER_PRESETS["Jacobi"])
            mg_core_options.update(MG_LEVEL_PRESETS["Standard-1D"])
            mg_core_options.update(MG_RESTRICTION_PRESETS["Standard-1D"])
            mg_core_options.update(MG_INTERPOLATION_PRESETS["Standard-1D"])
            mg_core_options["shape_coarse"] = 4
            mg_core_options["n_pre"] = 1
            mg_core_options["n_post"] = 1
            self.mg_core = MultiGridCore(self, lambda h: (kwargs['stencil_fnc'](h), np.array([1])), **mg_core_options)
            self.mg_core.levels[-1].mid[:] = next_x.reshape(self.mg_core.levels[-1].mid.shape)
            self.mg_core.levels[-1].rhs = kwargs['rhs'].reshape(self.mg_core.levels[-1].rhs.shape)
            self.mg_core.pad(-1)
            self.mg_core.modify_rhs(-1)

            self.mg_core.v_cycle()
            self.mg_core.v_cycle()

            # LOG.debug("input: %s --> %s" % (next_x.shape, self._mg_core.levels[-1].mid.shape))
            return self.mg_core.levels[-1].mid.reshape(next_x.shape)

        elif method == 'direct':
            if kwargs.get('solver') is None:
                # assert_named_argument('mg_level', kwargs, types=IMultigridLevel, descriptor="Multigrid Level",
                #                       checking_obj=self)
                assert_named_argument('stencil', kwargs, types=Stencil, descriptor="MG Stencil", checking_obj=self)
                solver_function = DirectSolverSmoother(kwargs['stencil'], kwargs['mg_level']).relax
            else:
                solver_function = kwargs['solver']
            # LOG.debug("next_x.shape: {:s}".format(next_x.shape))
            return solver_function(next_x)
        else:
            raise ValueError("Unknown method: '%s'" % method)
    def __init__(self, *args, **kwargs):
        """
        Parameters
        ----------
        rhs_function_wrt_space : :py:class:`callable`
            function returning the space-dependent values for the right hand side as used by the space solver
        boundaries : :py:class:`None` or :py:class:`list` of :py:class:`str`
            *(optional)*
            defaults to ``periodic`` for each dimension
        boundary_functions : :py:class:`None` or :py:class:`list` of :py:class:`callable`
            *(optional)*
            functions defined on the boundaries of the geometry
        geometry : :py:class:`None` or :py:class:`numpy.ndarray`
            *(optional)*
            specifying the dimension and extend of the geometry
        """
        assert_is_instance(self, IProblem, message="This Mixin is only valid for IProblems.", checking_obj=self)

        assert_named_argument('rhs_function_wrt_space', kwargs, descriptor="RHS for space solver", checking_obj=self)
        assert_is_callable(kwargs['rhs_function_wrt_space'], descriptor="RHS for space solver", checking_obj=self)

        self._rhs_function_wrt_space = kwargs['rhs_function_wrt_space']

        # check if boundary conditions are specified
        if kwargs.get('boundaries') is None:
            self._boundaries = ['periodic'] * len(self.spacial_dim)
        elif isinstance(kwargs['boundaries'], str) \
                and kwargs['boundaries'] in MultigridProblemMixin.valid_boundary_conditions:
            self._boundaries = [kwargs['boundaries']] * len(self.spacial_dim)
        elif isinstance(kwargs['boundaries'], list):
            check = 0
            for bc in kwargs['boundaries']:
                if bc in MultigridProblemMixin.valid_boundary_conditions:
                    check += 1
            if check == len(self.spacial_dim) * 2:
                self._boundaries = kwargs['boundaries']
            else:
                LOG.warning('Boundary specifications are not valid, will use periodic boundaries for each dimension.')
                self._boundaries = ['periodic'] * len(self.spacial_dim)
        else:
            LOG.warning('Boundary specifications are not valid, will use periodic boundaries for each dimension')
            self._boundaries = ['periodic'] * len(self.spacial_dim)

        # assign according to the boundary conditions the right functions
        if kwargs.get('boundary_functions') is None:
            self._boundary_functions = [None] * len(self.spacial_dim)
        else:
            assert_is_instance(kwargs['boundary_functions'], list, descriptor="Boundary Functions", checking_obj=self)
            check = 0
            assert_condition(len(kwargs['boundary_functions']) == len(self.spacial_dim),
                             ValueError, message="Not enough boundary functions given.", checking_obj=self)

            for ftpls in kwargs['boundary_functions']:
                if ftpls is 'dirichlet':
                    assert_is_instance(ftpls, list, message="Dirichlet function list not available", checking_obj=self)
                    assert_condition(len(ftpls) == 2,
                                     ValueError, message="Wrong number of functions", checking_obj=self)
                    assert_is_callable(ftpls[0], "Not a function", self)
                    assert_is_callable(ftpls[1], "Not a function", self)
                check += 1
            self._boundary_functions = kwargs['boundary_functions']

        # construct or save the geometry
        if kwargs.get('geometry') is None:
            self._geometry = np.asarray([[0, 1]] * len(self.spacial_dim))
        else:
            assert_is_instance(kwargs['geometry'], np.ndarray, descriptor="Geometry", checking_obj=self)
            assert_condition(len(kwargs["geometry"].shape) == 2, ValueError,
                             message="Numpy array has the wrong dimensions", checking_obj=self)
            assert_condition(kwargs['geometry'].shape[0] == len(self.spacial_dim) and kwargs['geometry'].shape[1] == 2,
                             ValueError,
                             message="Numpy array has a wrong shape", checking_obj=self)
            self._geometry = kwargs['geometry']

        self._rhs_space_operators = {}

        self._implicit_solve_method = kwargs.get('implicit_solve_method', 'direct')
        self._mg_core = None

        # the Space tensor which is actually used
        self._act_space_tensor = None
        self._act_grid_distances = None
        # the points actually used
        self._act_npoints = None
    def run(self, state, **kwargs):
        """Semi-Implicit Euler step method.

        .. math::

            u_{m+1}^{k+1} - \\Delta_\\tau F_I(t_{m+1}, u_{m+1}^{k+1}) =
                u_m^{k+1} &+ \\Delta_\\tau \\left( F_I(t_{m+1}, u_{m+1}^k)
                                                  - F_E(t_m, u_m^{k+1}) + F_E(t_m, u_m^k) \\right) \\\\
                          &+ \\Delta_t I_m^{m+1} \\left( F(\\vec{u}^k) \\right)

        Parameters
        ----------
        state : :py:class:`.SdcSolverState`

        Notes
        -----
        This step method requires the given problem to provide partial evaluation of the right-hand side.
        """
        super(SemiImplicitSdcCore, self).run(state, **kwargs)

        assert_is_instance(state, SdcSolverState, descriptor="State", checking_obj=self)
        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)
        _problem = kwargs['problem']

        _previous_iteration_current_step = self._previous_iteration_current_step(state)
        _previous_iteration_previous_step = self._previous_iteration_previous_step(state)

        if problem_has_direct_implicit(_problem, self):
            _sol = _problem.direct_implicit(phis_of_time=[_previous_iteration_previous_step.value,
                                                          _previous_iteration_current_step.value,
                                                          state.previous_step.value],
                                            delta_node=state.current_step.delta_tau,
                                            delta_step=state.current_time_step.delta_time_step,
                                            integral=state.current_step.integral)

        else:
            # Note: \Delta_t is always 1.0 as it's part of the integral
            _expl_term = \
                (state.previous_step.value
                 + state.current_step.delta_tau
                 * (_problem.evaluate_wrt_time(state.current_step.time_point,
                                               state.previous_step.value,
                                               partial="expl")
                    - _problem.evaluate_wrt_time(state.previous_step.time_point,
                                                 _previous_iteration_previous_step.value,
                                                 partial="expl")
                    - _problem.evaluate_wrt_time(state.current_step.time_point,
                                                 _previous_iteration_current_step.value,
                                                 partial="impl"))
                 + state.current_step.integral).reshape(-1)
            _func = lambda x_next: \
                _expl_term \
                + state.current_step.delta_tau \
                  * _problem.evaluate_wrt_time(state.current_step.time_point,
                                               x_next.reshape(_problem.dim_for_time_solver),
                                               partial="impl").reshape(-1) \
                - x_next
            _sol = _problem.implicit_solve(state.current_step.value.reshape(-1), _func,
                                           expl_term=_expl_term,
                                           time_level=0,
                                           delta_time=state.current_step.delta_tau).reshape(state.current_step.value.shape)

        if type(state.current_step.value) == type(_sol):
            state.current_step.value = _sol
        else:
            state.current_step.value = _sol[0]
    def run(self, state, **kwargs):
        """Semi-Implicit Euler step method.

        .. math::

            u_{m+1}^{k+1} - \\Delta_\\tau F_I(t_{m+1}, u_{m+1}^{k+1}) =
                u_m^{k+1} &+ \\Delta_\\tau \\left( F_I(t_{m+1}, u_{m+1}^k)
                                                  - F_E(t_m, u_m^{k+1}) + F_E(t_m, u_m^k) \\right) \\\\
                          &+ \\Delta_t I_m^{m+1} \\left( F(\\vec{u}^k) \\right)

        Parameters
        ----------
        state : :py:class:`.MlSdcSolverState`

        Notes
        -----
        This step method requires the given problem to provide partial evaluation of the right-hand side.
        """
        super(SemiImplicitMlSdcCore, self).run(state, **kwargs)

        assert_is_instance(state, MlSdcSolverState, descriptor="State", checking_obj=self)
        assert_named_argument('problem', kwargs, types=IProblem, descriptor="Problem", checking_obj=self)
        _problem = kwargs['problem']

        use_intermediate = kwargs['use_intermediate'] if 'use_intermediate' in kwargs else False

        if use_intermediate:
            # LOG.debug("using intermediate")
            _previous_iteration_current_step = state.current_iteration.current_level.current_step.intermediate
        elif not state.current_iteration.on_finest_level:
            _previous_iteration_current_step = state.current_iteration.current_level.current_step
        else:
            _previous_iteration_current_step = self._previous_iteration_current_step(state)
        if not _previous_iteration_current_step.rhs_evaluated:
            _previous_iteration_current_step.rhs = \
                _problem.evaluate_wrt_time(_previous_iteration_current_step.time_point,
                                           _previous_iteration_current_step.value)

        if not state.current_iteration.on_finest_level:
            _previous_iteration_previous_step = state.current_iteration.current_level.previous_step
        else:
            _previous_iteration_previous_step = self._previous_iteration_previous_step(state)
        if not _previous_iteration_previous_step.rhs_evaluated:
            _previous_iteration_previous_step.rhs = \
                _problem.evaluate_wrt_time(_previous_iteration_previous_step.time_point,
                                           _previous_iteration_previous_step.value)

        _fas = np.zeros(_previous_iteration_current_step.rhs.shape,
                        dtype=_previous_iteration_current_step.rhs.dtype)
        if not use_intermediate and _previous_iteration_current_step.has_fas_correction():
            # LOG.debug("   previous iteration current step has FAS: %s"
            #           % _previous_iteration_current_step.fas_correction)
            _fas = _previous_iteration_current_step.fas_correction

        if problem_has_direct_implicit(_problem, self):
            _sol = _problem.direct_implicit(phis_of_time=[_previous_iteration_previous_step.value,
                                                          _previous_iteration_current_step.value,
                                                          state.previous_step.value],
                                            delta_node=state.current_step.delta_tau,
                                            delta_step=state.delta_interval,
                                            integral=state.current_step.integral,
                                            fas=_fas,
                                            core=self)

        else:
            # Note: \Delta_t is always 1.0 as it's part of the integral
            _Fe_u_cp = _problem.evaluate_wrt_time(state.previous_step.time_point,
                                               state.previous_step.value,
                                               partial="expl")
            _Fe_u_pp = _problem.evaluate_wrt_time(_previous_iteration_previous_step.time_point,
                                                 _previous_iteration_previous_step.value,
                                                 partial="expl")
            _Fe_u_pc = _problem.evaluate_wrt_time(state.current_step.time_point,
                                                 _previous_iteration_current_step.value,
                                                 partial="impl")
            _expl_term = \
                (state.previous_step.value
                 + state.current_step.delta_tau
                 * (_Fe_u_cp - _Fe_u_pp - _Fe_u_pc)
                 + state.current_step.integral + _fas).reshape(-1)
            # LOG.debug("EXPL TERM: %s = %s + %f * (%s - %s - %s) + %s + %s"
            #           % (_expl_term, state.previous_step.value, state.current_step.delta_tau, _Fe_u_cp, _Fe_u_pp,
            #              _Fe_u_pc, state.current_step.integral, _fas))
            _func = lambda x_next: \
                _expl_term \
                + state.current_step.delta_tau * _problem.evaluate_wrt_time(state.current_step.time_point,
                                                                            x_next.reshape(_problem.dim_for_time_solver),
                                                                            partial="impl").reshape(-1) \
                - x_next
            # LOG.debug("shape of value: %s" % (state.current_step.value.shape,))
            # LOG.debug("shape expl term: %s" % (_expl_term.shape,))
            # LOG.debug("shape impl func: %s" % (_func(state.current_step.value.reshape(-1)).shape,))
            _sol = \
                _problem.implicit_solve(
                    state.current_step.value.reshape(-1),
                    _func,
                    expl_term=_expl_term,
                    time_level=state.current_iteration.current_level_index,
                    delta_time=state.current_iteration.current_level.current_step.delta_tau
                ).reshape(state.current_step.value.shape)

        if type(state.current_step.value) == type(_sol):
            state.current_step.value = _sol
        else:
            LOG.debug("Solution Type %s but expected %s" % (type(_sol), type(state.current_step.value)))
            state.current_step.value = _sol[0]
    def plot(self, *args, **kwargs):
        """Plots the solution and optional also the error for each iteration.

        If ``file_name`` has been specified on initialization (see :py:meth:`.IPlotter.__init__`) the plot is stored
        there.
        Otherwise an interactive plotting session is started.

        Parameters
        ----------
        solvers : :py:class:`.IIterativeTimeSolver`
            solver instances used to calculate the solutions

        states : :py:class:`.ISolverState`
            states of the solvers

        Raises
        ------
        ValueError

            * if ``solvers`` is not given or is not a :py:class:`numpy.ndarray` of :py:class:`.IIterativeTimeSolver`
            * if ``states`` is not given or is not a :py:class:`numpy.ndarray` of :py:class:`.ISolverState`
            * if ``states`` has more than 7 states
            * if the size of ``states`` does not equal the size of ``solvers``
        """
        super(ReductionResidualPlotter, self).plot(args, **kwargs)

        assert_named_argument('solvers', kwargs, types=np.ndarray, descriptor="Solver", checking_obj=self)
        [assert_is_instance(_solver, IIterativeTimeSolver, descriptor="All Solvers", checking_obj=self)
         for _solver in kwargs['solvers']]
        self._solvers = kwargs['solvers']

        assert_named_argument('states', kwargs, types=np.ndarray, descriptor="States", checking_obj=self)
        assert_condition(kwargs['states'].size <= 7,
                         ValueError, "Can only handle up to 7 solutions: %d" % kwargs['states'].size,
                         self)
        [assert_is_instance(_state, ISolverState,
                            descriptor="All States", checking_obj=self) for _state in kwargs['states']]
        self._states = kwargs['states']

        assert_condition(self._solvers.size == self._states.size,
                         ValueError, message="Number of solvers must equal number of states: %d != %d"
                                             % (self._solvers.size, self._states.size),
                         checking_obj=self)

        self._nodes = self._states[0].first.time_points

        if self._solvers[0].problem.time_start != self._nodes[0]:
            self._nodes = np.concatenate(([self._solvers[0].problem.time_start], self._nodes))
        if self._solvers[0].problem.time_end != self._nodes[-1]:
            self._nodes = np.concatenate((self._nodes, [self._solvers[0].problem.time_end]))

        plt.title("Residuals and Reduction per Iteration for different Lambdas")
        self._plot_residuals_reductions()

        if self._file_name is not None:
            fig = plt.gcf()
            fig.set_dpi(300)
            fig.set_size_inches((15., 15.))
            LOG.debug("Plotting figure with size (w,h) {:s} inches and {:d} DPI."
                      .format(fig.get_size_inches(), fig.get_dpi()))
            fig.savefig(self._file_name)

        if is_interactive():
            plt.show(block=True)
        else:
            plt.close('all')
    def plot(self, *args, **kwargs):
        """Plots the solution and optional also the error for each iteration.

        Parameters
        ----------
        solver : :py:class:`.IIterativeTimeSolver`
            solver instance used to calculate the solution

        state : :py:class:`.ISolverState`
            state containing information to plot

        errplot : :py:class:`bool`
            *(optional)*
            if given and :py:class:`True` also plots the errors for each iteration found in the solution

        residualplot : :py:class:`bool`
            *(optional)*
            if given and :py:class:`True` also plots the residual for each iteration found in the solution

        Raises
        ------
        ValueError

            * if ``solver`` not given and not an :py:class:`.IIterativeTimeSolver`
            * if ``state`` not given and not an :py:class:`.ISolverState`
        """
        super(SingleSolutionPlotter, self).plot(args, **kwargs)

        assert_named_argument('solver', kwargs, types=IIterativeTimeSolver, descriptor="Solver", checking_obj=self)
        assert_named_argument('state', kwargs, types=ISolverState, descriptor="State must be given", checking_obj=self)

        self._solver = kwargs['solver']
        self._state = kwargs['state']
        self._nodes = self._state.first.time_points

        _subplots = 1
        _curr_subplot = 0
        if 'errorplot' in kwargs and kwargs['errorplot']:
            _subplots += 1
            self._errplot = True
        if 'residualplot' in kwargs and kwargs['residualplot']:
            _subplots += 1
            self._residualplot = True

        if self._solver.problem.time_start != self._nodes[0]:
            self._nodes = np.concatenate(([self._solver.problem.time_start], self._nodes))
        if self._solver.problem.time_end != self._nodes[-1]:
            self._nodes = np.concatenate((self._nodes, [self._solver.problem.time_end]))

        if self._errplot or self._residualplot:
            plt.suptitle(r"after {:d} iterations; overall reduction: {:.2e}"
                         .format(len(self._state),
                                 supremum_norm(self._state.solution
                                               .solution_reduction(self._state.last_iteration_index))))
            _curr_subplot += 1
            plt.subplot(_subplots, 1, _curr_subplot)

        self._final_solution()
        plt.title(self._solver.problem.__str__())

        if self._errplot:
            _curr_subplot += 1
            plt.subplot(3, 1, _curr_subplot)
            self._error_plot()

        if self._residualplot:
            _curr_subplot += 1
            plt.subplot(3, 1, _curr_subplot)
            self._residual_plot()

        if self._file_name is not None:
            fig = plt.gcf()
            fig.set_dpi(300)
            fig.set_size_inches((15., 15.))
            LOG.debug("Plotting figure with size (w,h) {:s} inches and {:d} DPI."
                      .format(fig.get_size_inches(), fig.get_dpi()))
            fig.savefig(self._file_name)

        if is_interactive():
            plt.show(block=True)
        else:
            plt.close('all')