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']
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()
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)
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 = []
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
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) )
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']
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
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)
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
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)
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]
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')