def assert_is_key(key, dictionary, message=None, key_desc=None, dict_desc=None, checking_obj=None): """Asserts dictionary has a certain key Parameters ---------- key : ``key`` key to check dictionary : :py:class:`dict` dictionary to check message : :py:class:`str` *(optional)* message content of exception raised checking_obj : :py:class:`object` or :py:class:`None` *(optional)* The exception will be raised in the scope of the given object. If :py:class:`None` (default) no scope will be displayed. Raises ------ ValueError if ``key`` is not of a key in ``dictionary`` """ if key not in dictionary: if not message: if not key_desc: key_desc = "'{:s}'".format(key) if not dict_desc: dict_desc = "given dict" message = "{:s} is not a key in {:s}.".format(key_desc, dict_desc) LOG.critical(func_name(checking_obj) + message) LOG.debug(func_name(checking_obj) + "Keys in {:s}: {:s}".format(id(dictionary), ', '.join(dictionary.keys()))) raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
def assert_is_callable(obj, message=None, descriptor=None, checking_obj=None): """Asserts callability of given object Parameters ---------- obj : :py:class:`object` object to be asserted callable message : :py:class:`str` or :py:class:`None` *(optional)* checking_obj : :py:class:`object` or :py:class:`None` *(optional)* The exception will be raised in the scope of the given object. If :py:class:`None` (default) no scope will be displayed. Raises ------ ValueError if ``obj`` is not :py:class:`Callable` """ if not isinstance(obj, Callable): if not message: if descriptor: message = "{:s} must be callable.".format(descriptor) else: message = "Required a callable: NOT {:s}.".format(class_name(obj)) LOG.critical(func_name(checking_obj) + message) raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
def definalize(self): if self._finalized: self._solution = self.solution.__class__() self._finalized = False self.reset_to_start() else: LOG.debug("This {} wasn't finalized.".format(class_name(self)))
def __init__(self, *args, **kwargs): if "file_name" in kwargs and isinstance(kwargs["file_name"], str) and len(kwargs["file_name"]) > 0: self._file_name = kwargs["file_name"] LOG.debug("Non-interactive plotting. Saving to '{:s}'".format(self._file_name)) plt.ioff() else: self._file_name = None LOG.debug("Interactive plotting.") plt.ion()
def __init__(self, *args, **kwargs): super(self.__class__, self).__init__(*args, **kwargs) assert_condition(kwargs['num_fine_points'] % 2 != 0, ValueError, message="Number of fine level points needs to be odd: %d" % kwargs['num_fine_points'], checking_obj=self) self._n_coarse_points = int((self.num_fine_points + 1) / 2) self.restringation_operator = np.zeros([self.num_coarse_points, self.num_fine_points]) self.prolongation_operator = np.zeros([self.num_fine_points, self.num_coarse_points]) self._construct_transform_matrices() LOG.debug("Restringation operator: {}".format(self.restringation_operator)) LOG.debug("Prolongation operator: {}".format(self.prolongation_operator))
def assert_named_argument(name, kwargs, types=None, message=None, descriptor=None, checking_obj=None): if name not in kwargs: if not message: if descriptor: message = "%s ('%s') is a required argument." % (descriptor, name) else: message = "'%s' is a required argument." % name LOG.critical(func_name(checking_obj) + message) LOG.debug(func_name(checking_obj) + "Named arguments were: %s" % ', '.join(kwargs.keys())) raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message)) if types: assert_is_instance(kwargs[name], types, descriptor=descriptor, checking_obj=checking_obj)
def _plot_residuals_reductions(self): plt.hold(True) for _state in range(0, self._states.size): self._add_solution_plot(_state) LOG.debug("Plotting within {:s} x {:s}".format(self.__residual_limits, self.__reduction_limits)) _limits = [0.0, 0.0] _limits[0] = min(self.__residual_limits[0], self.__reduction_limits[0]) _limits[1] = max(self.__residual_limits[0], self.__reduction_limits[1]) plt.xlabel("residual") plt.xlim(_limits) plt.ylabel("reduction") plt.ylim(_limits) #plt.legend(loc=4) plt.grid(True)
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 implicit_solve(self, next_x, func, method="hybr", **kwargs): """A solver for implicit equations. Finds the implicitly defined :math:`x_{i+1}` for the given right hand side function :math:`f(x_{i+1})`, such that :math:`x_{i+1}=f(x_{i+1})`. Parameters ---------- next_x : :py:class:`numpy.ndarray` A starting guess for the implicitly defined value. rhs_call : :py:class:`callable` The right hand side function depending on the implicitly defined new value. method : :py:class:`str` *(optional, default=``hybr``)* Method fo the root finding algorithm. See `scipy.optimize.root <http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.root.html#scipy.optimize.root>` for details. Returns ------- next_x : :py:class:`numpy.ndarray` The calculated new value. Raises ------ ValueError : * if ``next_x`` is not a :py:class:`numpy.ndarray` of shape :py:attr:`.IProblem.dim` * if ``fun`` is not :py:class:`callable` * if computed solution is not a `:py:class:`numpy.ndarray` UserWarning : If the implicit solver did not converged, i.e. the solution object's ``success`` is not :py:class:`True`. """ assert_is_instance(next_x, np.ndarray, descriptor="Initial Guess", checking_obj=self) assert_is_callable(func, descriptor="Function of RHS for Implicit Solver", checking_obj=self) sol = find_root(fun=func, x0=next_x.reshape(-1), method=method) if not sol.success: warnings.warn("Implicit solver did not converged.") LOG.debug("sol.x: %s" % sol.x) LOG.error("Implicit solver failed: %s" % sol.message) else: assert_is_instance(sol.x, np.ndarray, descriptor="Solution", checking_obj=self) return sol.x.reshape(self.dim_for_time_solver)
def assert_is_instance(obj, instances, message=None, descriptor=None, checking_obj=None): """Asserts element is of certain type Parameters ---------- obj : :py:class:`object` object to check type instances : :py:class:`type` of ``classes`` or :py:class:`class` types to check message : :py:class:`str` *(optional)* message content of exception raised checking_obj : :py:class:`object` or :py:class:`None` *(optional)* The exception will be raised in the scope of the given object. If :py:class:`None` (default) no scope will be displayed. Raises ------ ValueError if ``obj`` is not of type ``instances`` """ if not isinstance(obj, instances): if not message: _instances_str = set() if isinstance(instances, (tuple, set, frozenset)): for _i in instances: _instances_str.add("'%s'" % _i.__name__) else: _instances_str.add("'%s'" % instances.__name__) if descriptor: if len(_instances_str) > 1: message = "{:s} must be one of {:s}: NOT {:s}"\ .format(descriptor, ', '.join(_instances_str), class_name(obj)) else: message = "{:s} must be a {:s}: NOT {:s}"\ .format(descriptor, ', '.join(_instances_str), class_name(obj)) else: if len(_instances_str) > 1: message = "Required one of {:s}: NOT {:s}.".format(', '.join(_instances_str), class_name(obj)) else: message = "Required a {:s}: NOT {:s}.".format(', '.join(_instances_str), class_name(obj)) LOG.critical(func_name(checking_obj) + message) raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
def transform(self, interval): """Transforms computed integration nodes to fit a new given interval. Based on the old interval the computed integration nodes are transformed fitting the newly given interval using standard linear interval scaling. In case no interval was previously given, the standard interval of the used nodes method, e.g. :math:`[-1, 1]` for Gauss-Lobatto, is used. Parameters ---------- interval : :py:class:`numpy.ndarray(size=2)` New interval to transform nodes onto. Raises ------ ValueError If the standard interval is not suited for transformation, i.e. it is not a :py:class:`numpy.ndarray` of size 2 and not positive. Notes ----- It may be this transformation is numerically inconvenient because of the loss of significance. """ assert_is_instance(interval, np.ndarray, descriptor="Interval", checking_obj=self) assert_condition(interval.size == 2, ValueError, message="Intervals must be of size 2: {} ({:s})".format(interval, class_name(interval)), checking_obj=self) assert_condition(interval[0] < interval[1], ValueError, message="Interval must be positive: {:.2f} > {:.2f}".format(interval[0], interval[1]), checking_obj=self) _old_interval = self.interval self._interval = interval self._nodes = (self.nodes - _old_interval[0]) * (interval[1] - interval[0]) / \ (_old_interval[1] - _old_interval[0]) + interval[0] assert_condition(self._nodes[0] - self._interval[0] <= 1e-16 and self._nodes[-1] - self._interval[1] <= 1e-16, RuntimeError, message="Newly computed nodes do not match new interval: {} NOT IN {}" .format(self._nodes, self._interval), checking_obj=self) LOG.debug("Nodes: %s" % self._nodes.tolist())
def assert_is_in(element, test_list, message=None, elem_desc=None, list_desc=None, checking_obj=None): """Asserts element in list or sequence Parameters ---------- element : :py:class:`object` element to check membership test_list : ``Sequence`` sequence to check message : :py:class:`str` *(optional)* message content of exception raised checking_obj : :py:class:`object` or :py:class:`None` *(optional)* The exception will be raised in the scope of the given object. If :py:class:`None` (default) no scope will be displayed. Raises ------ ValueError if ``element`` is not in ``test_list`` Examples -------- assert_is_in(True, bool_list, elem_desc='', list_desc='', checking_obj=self) """ if element not in test_list: if not message: if not list_desc: list_desc = class_name(test_list) if not elem_desc: elem_desc = "Element {:r}".format(element) message = "{:s} is not in {:s}.".format(elem_desc, list_desc) LOG.critical(func_name(checking_obj) + message) LOG.debug(func_name(checking_obj) + "Elements in {:s}: {:s}".format(class_name(test_list), ', '.join(test_list))) raise ValueError("{:s}.{:s}(): {:s}".format(checking_obj_name(checking_obj), inspect.stack()[2][3], message))
def compute_reduction(self, state): """Computes the reduction of the error and solution With respect to the supremum nomr of the given state's current iteration (see :py:attr:`.ISolverState.current_iteration` and :py:class:`.IIterationState`). In case no previous iteration is available, it immediatly returns. """ if not state.previous_iteration: # there is no previous iteration to compare with LOG.debug("Skipping computation of reduction: No previous iteration available.") return if state.current_iteration.final_step.solution.error: # error is given; computing reduction of it _previous_error = supremum_norm(state.previous_iteration.final_step.solution.error) _current_error = supremum_norm(state.current_iteration.final_step.solution.error) state.solution.set_error_reduction(state.current_iteration_index, abs((_previous_error - _current_error) / _previous_error * 100)) # computing reduction of solution _previous_solution = supremum_norm(state.previous_iteration.final_step.solution.value) _current_solution = supremum_norm(state.current_iteration.final_step.solution.value) state.solution.set_solution_reduction(state.current_iteration_index, abs((_previous_solution - _current_solution) / _previous_solution * 100))
def _check(self, operator, name, value): _value = supremum_norm(value) if isinstance(value, (IDiagnosisValue, np.ndarray)) else value if name in self._conditions and self._conditions[name] is not None: assert_condition(_value is not None, ValueError, message="'{:s}' is a termination condition but not available to check." .format(name[0].capitalize() + name[1:]), checking_obj=self) if operator == "min": if _value <= self._conditions[name]: LOG.debug("Minimum of {:s} reached: {:.2e} <= {:.2e}" .format(name, _value, self._conditions[name])) self._reason.append(name) elif operator == "max": if _value >= self._conditions[name]: LOG.debug("Maximum of {:s} exceeded: {:d} >= {:d}" .format(name, _value, self._conditions[name])) self._reason.append(name) else: raise ValueError("Given operator '{:s}' is invalid.".format(operator)) else: # $name is not a condition pass
def assert_condition(condition, exception_type, message, checking_obj=None): """Asserts trueness of arbitrary condition Parameters ---------- condition : :py:class:`bool` or ``boolean expression`` expression to be asserted exception_type : :py:class:`Exception` type of exception to be raised if ``condition`` evaluates to :py:class:`False` message : :py:class:`str` message content of exception raised checking_obj : :py:class:`object` or :py:class:`None` The exception will be raised in the scope of the given object. If :py:class:`None` (default) no scope will be displayed. Raises ------ exception_type if ``condition`` evaluates to :py:class:`False` """ if not condition: LOG.critical(func_name(checking_obj) + message) raise exception_type("{:s}.{:s}(): {:s}" .format(checking_obj_name(checking_obj), inspect.stack()[1][3], message))
def transform_interval(self, interval): """Transforms nodes onto new interval See Also -------- :py:meth:`.IntegratorBase.transform_interval` : overridden method """ if interval is not None: if interval[1] - interval[0] != self.nodes[-1] - self.nodes[0]: LOG.debug("Size of interval changed. Recalculating weights.") super(SdcIntegrator, self).transform_interval(interval) self._construct_s_matrix() else: super(SdcIntegrator, self).transform_interval(interval) LOG.debug("Size of interval did not change. Don't need to recompute S and Q matrices") else: LOG.debug("Cannot transform interval to None. Skipping.")
# coding=utf-8 """ .. moduleauthor:: Torbjörn Klatt <*****@*****.**> .. moduleauthor:: Dieter Moser <*****@*****.**> """ from collections import OrderedDict import numpy as np dt = 0.001 from pypint.utilities.logging import LOG, print_logging_message_tree, VERBOSITY_LVL1, SEPARATOR_LVL1, SEPARATOR_LVL2 LOG.info("%sSetting Up Multigrid Space Solver" % VERBOSITY_LVL1) from pypint.plugins.multigrid.stencil import Stencil laplace_stencil = Stencil(np.array([1.0, -2.0, 1.0]), None, 2) LOG.info("%s Laplace Discretization Stencil: %s" % (VERBOSITY_LVL1, laplace_stencil.arr)) LOG.info(SEPARATOR_LVL2) LOG.info("%sSetting Up 1D Heat Equation" % VERBOSITY_LVL1) LOG.info("%s Setting Up Geometry" % VERBOSITY_LVL1) geo = np.asarray([[0, 1]]) LOG.info("%s Setting Up Boundary Functions" % VERBOSITY_LVL1) boundary_types = ['dirichlet'] * 2 def left_f(x): _s = np.zeros(x.shape) LOG.debug("Left Bnd Fnc: %s -> %s" % (x.reshape(-1), _s.reshape(-1))) return _s
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 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 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 definalize(self): if self.finalized: self._finalized = False else: LOG.warning("This Solution is not finalized.")
def right_f(x): _s = np.zeros(x.shape) LOG.debug("Right Bnd Fnc: %s -> %s" % (x.reshape(-1), _s.reshape(-1))) return _s
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')