def problem_has_exact_solution(problem, checking_obj=None): """Convenience accessor for exact solution. Parameters ---------- problem : :py:class:`.IProblem` The problem to check for an exact solution function. checking_obj : object *(optional)* The object calling this function for a meaningful error message. For debugging purposes only. Returns ------- has_exact_solution : :py:class:`bool` :py:class:`True` if exact solution was given, :py:class:`False` otherwise Raises ------ ValueError : If the given problem is not an instance of :py:class:`.IProblem`. """ assert_is_instance( problem, IProblem, message="It needs to be a problem to have an exact solution.", checking_obj=checking_obj ) return isinstance(problem, HasExactSolutionMixin)
def time_interval(self, value): assert_is_instance(value, np.ndarray, descriptor="Time Interval", checking_obj=self) assert_condition(value.size == 2, ValueError, message="Time Interval must have two values: NOT %d" % value.size) self.validate_time_interval(start=value[0], end=value[1]) self.time_start = value[0] self.time_end = value[1]
def __init__(self, restriction_stencil, decrease_in_points=None, center=None): assert_is_instance(restriction_stencil, np.ndarray, "Not an ndarray") self.rst_stencil = Stencil(restriction_stencil, center) if decrease_in_points is None: self.dip = np.asarray(self.rst_stencil.shape) - 1 elif isinstance(decrease_in_points, np.ndarray) and \ decrease_in_points.ndim == restriction_stencil.ndim: self.dip = decrease_in_points elif isinstance(decrease_in_points, int): self.dip = np.asarray([decrease_in_points]*restriction_stencil.ndim) else: raise ValueError("Wrong decrease in points") self.dim = restriction_stencil.ndim if self.dim == 1: self.eval = self.evalA_1D elif self.dim == 2: self.eval = self.evalA_2D elif self.dim == 3: self.eval = self.evalA_3D else: raise NotImplementedError("More than 3 dimensions " + "are not implemented")
def __init__(self, *args, **kwargs): """ Parameters ---------- exact_function : :py:class:`callable` *(optional)* If given initializes the problem with the exact solution function. """ assert_is_instance( self, IProblem, descriptor="Problem needs to be a IProblem first: NOT %s" % class_name(self), checking_obj=self, ) self._exact_function = None if "exact_function" in kwargs: self.exact_function = kwargs["exact_function"] self._strings["exact"] = None if "strings" in kwargs: if "exact" in kwargs["strings"]: assert_is_instance( kwargs["strings"]["exact"], str, descriptor="String representation of Exact Function", checking_obj=self, ) self._strings["exact"] = kwargs["strings"]["exact"]
def add_level_transition(self, transitioner, coarse_level, fine_level): """Adds specialized level transitioner for specified levels. Parameters ---------- transitioner : :py:class:`.ILevelTransitionProvider` Special level transitioner for specified prolongation and restringation between given coarse and fine level. coarse_level : :py:class:`int` Coarse level of the transitioner. fine_level : :py:class:`int` Fine level of the transitioner. Raises ------ ValueError if ``transitioner`` is not an :py:class:`.ILevelTransitionProvider` """ assert_is_instance(transitioner, ILevelTransitionProvider, descriptor="Level Transitioner", checking_obj=self) # extend/initialize level_transition_provider map if necessary if coarse_level not in self._level_transitioners: self._level_transitioners[coarse_level] = {} self._level_transitioners[coarse_level][fine_level] = transitioner
def __init__(self, *args, **kwargs): super(HeatEquation, self).__init__(*args, **kwargs) # HasExactSolutionMixin.__init__(self, *args, **kwargs) self._thermal_diffusivity = kwargs.get('thermal_diffusivity', 1.0) if self.time_start is None: self.time_start = 0.0 if self.time_end is None: self.time_end = 1.0 if self.initial_value is None: self.initial_value = np.zeros(self.dim_for_time_solver) if isinstance(self.thermal_diffusivity, complex): self.numeric_type = np.complex self._mg_stencil = kwargs.get('mg_stencil') self._mg_level = kwargs.get('mg_level') self._direct_solvers = {} if kwargs.get('delta_times_for_time_levels') is not None and self._mg_level is not None: assert_is_instance(kwargs['delta_times_for_time_levels'], (list, np.ndarray), descriptor="Delta Times for Time Levels", checking_obj=self) for time_level in kwargs['delta_times_for_time_levels']: assert_is_instance(kwargs['delta_times_for_time_levels'][time_level], (list, np.ndarray), descriptor="Delta Times for Time Level %d" % time_level, checking_obj=self) for delta_time in kwargs['delta_times_for_time_levels'][time_level]: self.initialize_direct_space_solver(time_level, delta_time, kwargs['mg_level'])
def write_buffer(self, tag=None, **kwargs): """Writes data into this communicator's buffer Parameters ---------- value : data values to be send to the next solver time_point : :py:class:`float` time point of the data values flag : :py:class:`.Message.SolverFlag` message flag Raises ------ ValueError * if no arguments are given * if ``time_point`` is not a :py:class:`float` """ assert_condition(len(kwargs) > 0, ValueError, "At least one argument must be given.", self) if tag not in self._buffer: self._buffer[tag] = Message() self.write_buffer(tag=tag, **kwargs) if "value" in kwargs: self._buffer[tag].value = deepcopy(kwargs["value"]) if "time_point" in kwargs: assert_is_instance(kwargs["time_point"], float, descriptor="Time Point", checking_obj=self) self._buffer[tag].time_point = deepcopy(kwargs["time_point"]) if "flag" in kwargs: self._buffer[tag].flag = deepcopy(kwargs["flag"])
def __init__(self, stencil_list, center=None, *args, **kwargs): """init """ # in the case of just one stencil if isinstance(stencil_list, np.ndarray) and stencil_list.ndim == 1: self.mode = "own" self.increase_of_points = 2 self.stencil_list = [] self.stencil_list.append(stencil_list) self.center = [] if center is None: self.center.append(np.floor(np.asarray(self.stencil[0].shape)*0.5)) elif isinstance(center, int) and center < self.stencil[0].size: self.center.append(center) else: raise ValueError("Wrong argument") # now the case of a stencil set elif isinstance(stencil_list, list): # check if each one is a nd.array for stencil in stencil_list: assert_is_instance(stencil, Stencil, "not real stencil", self) # each stencil self.mode = "list" self.stencil_list = stencil_list self.increase_of_points = len(stencil_list) if self.mode == "own": self.eval = self.eval_own elif self.mode == "list": self.eval = self.eval_list else: raise RuntimeError("Something went terribly wrong") super().__init__(*args, **kwargs)
def problem_has_direct_implicit(problem, checking_obj=None): """Convenience checker for existence of a direct implicit formulation of a problem. Parameters ---------- problem : :py:class:`.IProblem` The problem to check for a direct implicit formulation. checking_obj : :py:class:`object` *(optional)* The object calling this function for a meaningful error message. For debugging purposes only. Returns ------- has_direct_impl : :py:class:`bool` :py:class:`True` if exact solution was given, :py:class:`False` otherwise Raises ------ ValueError : If the given problem is not an instance of :py:class:`.IProblem`. """ assert_is_instance(problem, IProblem, message="It needs to be a problem to have a direct implicit formula.", checking_obj=checking_obj) return isinstance(problem, HasDirectImplicitMixin)
def evaluate(self, data, **kwargs): """Applies this integrator to given data in specified time interval. Parameters ---------- data : :py:class:`numpy.ndarray` Data vector of the values at given time points. Its length must equal the number of integration nodes. time_start : :py:class:`float` *(optional)* Begining of the time interval to integrate over. time_end : :py:class:`float` *(optional)* End of the time interval to integrate over. Raises ------ ValueError : * if ``data`` is not a :py:class:`numpy.ndarray` * if either ``time_start`` or ``time_end`` are not given * if ``time_start`` is larger or equals ``time_end`` """ assert_is_instance(data, np.ndarray, descriptor="Data to integrate", checking_obj=self) assert_condition("time_start" in kwargs or "time_end" in kwargs, ValueError, message="Either start or end of time interval need to be given.", checking_obj=self) assert_condition(kwargs["time_start"] < kwargs["time_end"], ValueError, message="Time interval need to be non-zero positive: [{:f}, {:f}]" .format(kwargs["time_start"], kwargs["time_end"]), checking_obj=self)
def add_coefficient(self, coefficient, power): """Adds or sets the coefficient :math:`c` of :math:`cx^p` for a specific :math:`p`. The polynomial gets automatically extended to hold the new coefficient in case it didn't included the specified power previously. Unset, but skipped powers have a coefficient of zero by default. Parameters ---------- coefficient : :py:class:`float` Coefficient :math:`c` of :math:`cx^p`. power : :py:class:`int` Power :math:`p` of :math:`cx^p`. Examples -------- >>> polyWeights = PolynomialWeightFunction() >>> # To set the coefficient of x^3 to 3.14 use: >>> polyWeights.add_coefficient(3.14, 3) >>> # Similar, to set the constant coefficient 42, e.i. 42*x^0, use: >>> polyWeights.add_coefficient(42, 0) """ assert_is_instance(power, int, descriptor="Power", checking_obj=self) assert_condition(power >= 0, ValueError, message="Power must be zero or positive: {:d}".format(power), checking_obj=self) if self._coefficients.size <= power + 1: self._coefficients = np.resize(self._coefficients, (power + 1)) self._coefficients[power] = coefficient
def value(self, value): assert_condition(not self.finalized, AttributeError, message="Cannot change this solution data storage any more.", checking_obj=self) assert_is_instance(value, np.ndarray, descriptor="Values", checking_obj=self) self._dim = value.shape self._numeric_type = value.dtype self._data = value
def construct_space_tensor(self, number_of_points_list, stencil=None): """Constructs the Spacetensor which is important for the evaluation in the case of Dirichlet boundary conditions Parameters ---------- number_of_points_list : :py:class:`int` or :py:class:`numpy.ndarray` Number of points which will be distributed equiv-spaced on the grid """ if isinstance(number_of_points_list, (int, float, complex)): assert_is_instance(stencil, Stencil, descriptor="Stencil", checking_obj=self) npoints = int(number_of_points_list) # LOG.debug("Your number %s was modified to %s" % (number_of_points_list, npoints)) assert_condition(npoints > max(stencil.arr.shape), ValueError, message="Not enough points for the stencil", checking_obj=self) npoints = np.asarray([npoints] * len(self.spacial_dim)) elif isinstance(number_of_points_list, np.ndarray): assert_condition(len(number_of_points_list.shape) == 1 and number_of_points_list.size == len(self.spacial_dim), ValueError, message="The number_of_points list is wrong", checking_obj=self) npoints = np.floor(number_of_points_list) else: raise ValueError("Wrong number of points list") # first we assign the memory using numpy # spt(npoints,dim) self._act_npoints = npoints lspc = [] for i in range(len(self.spacial_dim)): lspc.append(np.linspace(self._geometry[i, 0], self._geometry[i, 1], npoints[i])) if len(self.spacial_dim) > 1: space_tensor = np.asarray(np.meshgrid(*lspc)) else: space_tensor = np.linspace(self._geometry[0, 0], self._geometry[0, 1], npoints) return space_tensor
def find_root(fun, x0, method="hybr"): """Wrapper around SciPy's generic root finding algorithm to support complex numbers. SciPy's generic root finding algorithm (``scipy.optimize.root``) is not able to deal with functions returning and/or accepting arrays with complex numbers. This wrapped call will first convert all arrays of complex numbers into arrays of floats while splitting each complex number up into two floats. Parameters ---------- fun : :py:class:`callable` Complex function to find the root of x0 : :py:class:`numpy.ndarray` Initial guess. method : :py:class:`str` Root finding method to be used. See ``scipy.optimize.root`` for details. Returns ------- Same solution object as ``scipy.optimize.root`` but with ``x`` being converted back including complex numbers. Examples -------- from pypint.plugins.implicit_solvers.find_root import find_root import numpy fun = lambda x: (-1.0 + 1.0j) * x sol = find_root(fun, numpy.array([0.0])) """ assert_is_instance(x0, np.ndarray, descriptor="Initial Guess") assert_is_callable(fun, descriptor="Function to find root of") assert_is_instance(method, str, descriptor="Root finding method") _value_map = {} _transformed_size = 0 _transform_necessary = False for i in range(0, x0.size): if isinstance(x0[i], complex): _value_map[i] = [_transformed_size, _transformed_size + 1] _transformed_size += 2 _transform_necessary = True else: _value_map[i] = [_transformed_size] _transformed_size += 1 if _transform_necessary: _wrapped_func = \ lambda x_next: _transform_to_real(fun(_transform_to_complex(x_next, _value_map)), _value_map, _transformed_size) sol = root(fun=_wrapped_func, x0=_transform_to_real(x0, _value_map, _transformed_size), method=method) else: sol = root(fun=fun, x0=x0, method=method) if sol.success and _transform_necessary: sol.x = _transform_to_complex(sol.x, _value_map) return sol
def implicit_solve(self, next_x, func, method="unused", **kwargs): """A solver for the implicit equations. """ 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 = scop.newton_krylov(func, next_x.reshape(-1)) assert_is_instance(sol, np.ndarray, descriptor="Solution", checking_obj=self) return sol.reshape(self.dim_for_time_solver)
def _init_new_interval(self, start): """Initializes a new work interval Parameters ---------- start : :py:class:`float` start point of new interval Returns ------- has_work : :py:class:`bool` :py:class:`True` if new interval have been initialized; :py:class:`False` if no new interval have been initialized (i.e. new interval end would exceed end of time given by problem) """ assert_is_instance(start, float, descriptor="Time Point", checking_obj=self) if start + self._dt > self.problem.time_end: return False if self.state and start == self.state.initial.time_point: return False self._init_new_state() # set width of current interval self.state.delta_interval = self._dt # compute time step and node distances self._deltas["t"] = self.state.delta_interval / self.num_time_steps # width of a single time step (equidistant) # start time points of time steps self.__time_points["steps"] = np.linspace(start, start + self._dt, self.num_time_steps + 1) # initialize and transform integrator for time step width self._integrator.init( self.__nodes_type, self.__num_nodes, self.__weights_type, interval=np.array([self.__time_points["steps"][0], self.__time_points["steps"][1]], dtype=np.float), ) self.__time_points["nodes"] = np.zeros((self.num_time_steps, self.num_nodes), dtype=np.float) _deltas_n = np.zeros(self.num_time_steps * (self.num_nodes - 1) + 1) # copy the node provider so we do not alter the integrator's one _nodes = deepcopy(self._integrator.nodes_type) for _t in range(0, self.num_time_steps): # transform Nodes (copy) onto new time step for retrieving actual integration nodes _nodes.interval = np.array([self.__time_points["steps"][_t], self.__time_points["steps"][_t + 1]]) self.__time_points["nodes"][_t] = _nodes.nodes.copy() for _n in range(0, self.num_nodes - 1): _i = _t * (self.num_nodes - 1) + _n _deltas_n[_i + 1] = _nodes.nodes[_n + 1] - _nodes.nodes[_n] self._deltas["n"] = _deltas_n[1:].copy() return True
def run(self, state, **kwargs): """Apply the solver core to the current state Parameters ---------- state : :py:class:`.ISolverState` Current state of the solver. """ assert_is_instance(state, ISolverState, descriptor="Solver's State", checking_obj=self)
def compute_error(self, state, **kwargs): """Computes the error of the current state Parameters ---------- state : :py:class:`.ISolverState` Current state of the solver. """ assert_is_instance(state, ISolverState, descriptor="Solver's State", checking_obj=self)
def __lt__(self, other): assert_is_instance(other, StepSolutionData, message="Can not compare StepSolutionData with {}".format(class_name(other)), checking_obj=self) return ( self.dim == other.dim and self.numeric_type == other.numeric_type and self.time_point < other.time_point )
def initial_value(self, initial_value): assert_is_instance(initial_value, np.ndarray, descriptor="Initial Value", checking_obj=self) assert_condition( initial_value.shape == self.dim_for_time_solver, ValueError, message="Initial Values shape must match problem DOFs: %s != %s" % (initial_value.shape, self.dim_for_time_solver), checking_obj=self, ) self._initial_value = initial_value
def __init__(self, *args, **kwargs): self._n = kwargs.get('n') self._m = 2*self._n + 1 kwargs.update({"dim": (self._m, self._m, 1)}) super(AvilesGiga, self).__init__(*args, **kwargs) # HasDirectImplicitMixin.__init__(self, *args, **kwargs) self._epsilon = kwargs.get('epsilon', 1.0) lspc = np.linspace(0, np.pi, self._m) x = np.meshgrid(lspc, lspc) if self.time_start is None: self.time_start = 0.0 if self.time_end is None: self.time_end = 1.0 if self.initial_value is None: if kwargs.get("initial") is "rand": self.initial_value = np.random.rand(self.dim_for_time_solver) * 1e-3 else: self.initial_value = (np.sin(x[1])*np.sin(x[0])).reshape(self.dim_for_time_solver) if isinstance(self.epsilon, complex): self.numeric_type = np.complex self._u = np.zeros((self._m, self._m), dtype=np.complex128) # place to work on self._u_x = self._u.copy() self._u_y = self._u.copy() self._u_f = self._u.copy() self._u_angle = self._u.copy() self._u_edens = self._u.copy() self._B = self._u.copy() # arrays to work in fourier space self._k_od = np.hstack((np.arange(self._n+1, dtype=np.int64), np.arange(self._n, dtype=np.int64)-self._n)) self._k_y = self._k_od self._k_x = self._k_od.reshape(self._m, 1) for i in range(self._m-1): self._k_y = np.vstack((self._k_y, self._k_od)) self._k_x = np.hstack((self._k_x, self._k_od.reshape(self._m, 1))) self._k_2 = self._k_x**2 + self._k_y**2 self._k_4 = self._k_2**2 if kwargs.get('delta_times_for_time_levels') is not None and self._mg_level is not None: assert_is_instance(kwargs['delta_times_for_time_levels'], (list, np.ndarray), descriptor="Delta Times for Time Levels", checking_obj=self) for time_level in kwargs['delta_times_for_time_levels']: assert_is_instance(kwargs['delta_times_for_time_levels'][time_level], (list, np.ndarray), descriptor="Delta Times for Time Level %d" % time_level, checking_obj=self) for delta_time in kwargs['delta_times_for_time_levels'][time_level]: self.initialize_direct_space_solver(time_level, delta_time, kwargs['mg_level'])
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 __eq__(self, other): assert_is_instance(other, StepSolutionData, message="Can not compare StepSolutionData with {}".format(class_name(other)), checking_obj=self) return ( self.time_point == other.time_point and self.dim == other.dim and self.numeric_type == other.numeric_type and np.array_equal(self.value, other.value) and self.error == other.error and self.residual == other.residual )
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 _init_new_interval(self, start): """Initializes a new work interval Parameters ---------- start : :py:class:`float` start point of new interval Returns ------- has_work : :py:class:`bool` :py:class:`True` if new interval have been initialized; :py:class:`False` if no new interval have been initialized (i.e. new interval end would exceed end of time given by problem) """ assert_is_instance(start, float, descriptor="Time Point", checking_obj=self) if start + self._dt > self.problem.time_end: return False if self.state and start == self.state.initial.time_point: return False self._init_new_state() # set width of current interval self.state.initial.solution.time_point = start self.state.initial.value = self.problem.initial_value.copy() self.state.delta_interval = self._dt self.__time_points = np.zeros(self.ml_provider.num_levels, dtype=np.object) self.__deltas = np.zeros(self.ml_provider.num_levels, dtype=np.object) # make sure the integrators are all set up correctly for the different levels for _level in range(0, self.ml_provider.num_levels): _integrator = self.ml_provider.integrator(_level) _integrator.transform_interval(self.state.interval) # print("nodes: %s" % _integrator.nodes) self.__time_points[_level] = np.zeros(_integrator.num_nodes, dtype=np.float) self.__deltas[_level] = np.zeros(_integrator.num_nodes, dtype=np.float) for _node in range(0, _integrator.num_nodes - 1): self.__time_points[_level] = deepcopy(_integrator.nodes) self.__deltas[_level][_node + 1] = _integrator.nodes[_node + 1] - _integrator.nodes[_node] # print("Time Points: %s" % self.__time_points) return True
def __init__(self, fine_level, coarse_level, rst_stencil, ipl_stencil): assert_is_instance(fine_level, MultigridLevel1D, "Not an MultigridLevel1D") assert_is_instance(coarse_level, MultigridLevel1D, "Not an MultigridLevel1D") self.fl = fine_level self.cl = coarse_level assert_is_instance(ipl_stencil, InterpolationByStencilListIn1D) assert_is_instance(rst_stencil, RestrictionStencilPure) assert_condition(rst_stencil.ndim == 1, ValueError, "Restriction Stencil" + "has not the dimension 1") self.ipl = ipl_stencil self.rst = rst_stencil self.ipl_fine_views = [] self.ipl_coarse_views = [] # collect the views which are needed, if self.ipl.mode == "own": self.ipl_fine_views.append(self.fl.evaluable_view( self.ipl.stencil_list[0])) self.ipl_coarse_views(self.cl.mid) elif self.ipl.mode == "list": for stencil in self.ipl.stencil_list: self.ipl_fine_views.append(self.fl.evaluable_view(stencil)) self.ipl_coarse_views.append(self.cl.mid) else: raise NotImplementedError("What do you have in mind?") self.rst_fine_view = self.fl.evaluable_view(self.rst) self.rst_coarse_view = self.cl.mid
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 __init__(self, value): """ Parameters ---------- value : :py:class:`numpy.ndarray` Raises ------ ValueError: If ``value`` is not a :py:class:`numpy.ndarray`. """ assert_is_instance(value, np.ndarray, descriptor="Diagnosis Values", checking_obj=self) self._data = value self._numeric_type = self.value.dtype
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 initialize_direct_space_solver(self, time_level, delta_time, mg_level=None): if mg_level is None: mg_level = self._mg_level assert_is_instance(mg_level, IMultigridLevel, descriptor="Multigrid Level", checking_obj=self) _stencil = Stencil(self.mg_stencil(delta_time, mg_level.h)) # LOG.debug("Stencil for dt=%f, h=%f: %s" % (delta_time, mg_level.h, _stencil.arr)) time_level = str(time_level) delta_time = str(delta_time) if self._direct_solvers.get(time_level) is None: # LOG.debug("Initializing Solvers for Time Level '%s'" % time_level) self._direct_solvers[time_level] = {} # LOG.debug(" Initializing Solver for Time Level '%s' and Delta Node '%s'" % (time_level, delta_time)) # LOG.debug(" shape: %s" % (mg_level.mid.shape)) self._direct_solvers[time_level][delta_time] = { 'mg_level': mg_level, 'stencil': _stencil, 'solver': _stencil.generate_direct_solver(mg_level.mid.shape) }