Пример #1
0
    def __init__(self, rst_stencil, level_in, level_out, *args, pre_assign=None, **kwargs):
        super(RestrictionByStencilForLevels, self).__init__(*args, **kwargs)
        if pre_assign is None:
            # no pre assignment function
            self.pre_assign = lambda a, b: b
        else:
            assert_is_callable(pre_assign, "Pre assignment function is not callable")
            self.pre_assign = pre_assign

        assert_is_instance(rst_stencil, Stencil, "Not a Stencil")
        assert_is_instance(level_in, IMultigridLevel, "Not a IMultigridLevel")
        assert_is_instance(level_out, IMultigridLevel, "Not a IMultigridLevel")
        self.l_in = level_in
        self.l_out = level_out
        self.rst_stencil = rst_stencil
        # check if the number of points per level matches
        self.dip = []
        for i in range(rst_stencil.dim):
            self.dip.append((level_in.mid.shape[i]-1)/(level_out.mid.shape[i]-1) - 1)
            # print("in.shape[", i, "]: ", level_in.mid.shape[i])
            # print("out.shape[", i, "]: ", level_out.mid.shape[i])
            # print("dip[", i, "]: ", self.dip[-1])
            if (self.dip[-1] % 1) != 0:
                raise ValueError("The Level do not match in direction " + str(i))

        # now just construct a slice tuple and the evaluable view from the finer grid
        self.evaluable_view = level_in.evaluable_restriction_view(rst_stencil)
        self.slices = []
        for i in range(rst_stencil.dim):
            self.slices.append(slice(None, None, self.dip[i]+1))
Пример #2
0
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
Пример #3
0
 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)
Пример #4
0
    def __init__(self, level_in, level_out, stencil_list, *args, pre_assign=None, **kwargs):
        """init
        """
        super(InterpolationByStencilForLevelsClassical, self).__init__(*args, **kwargs)

        if pre_assign is None:
            # no pre assignment function
            self.pre_assign = lambda a, b: b
        else:
            assert_is_callable(pre_assign, "Pre assignment function is not callable")
            self.pre_assign = pre_assign

        # check if all parameters are fitting
        assert_is_instance(stencil_list, list)

        for st in stencil_list:
            assert_is_instance(st[0], Stencil, "that is not a stencil")
        self.stencil_list = stencil_list
        assert_is_instance(level_in, IMultigridLevel, "Not a IMultigridLevel")
        assert_is_instance(level_out, IMultigridLevel, "Not a IMultigridLevel")
        self.level_in = level_in
        self.level_out = level_out
        # increase in points for each direction
        self.iip = []
        self.fits = False

        for i in range(level_in.mid.ndim):

            self.iip.append((level_out.mid.shape[i]-1)/(level_in.mid.shape[i]) - 1)

            n = level_out.mid.shape[i]
            m = level_in.mid.shape[i]
            self.fits = False

            while m <= n:
                if m == n:
                    self.fits = True
                    break
                else:
                    m = m*2+1

            if not self.fits:
                raise ValueError("The Levels do not match in direction " + str(i))

        # compute evaluable views with the positions and slices

        self.slices_out = []
        for st, pos in stencil_list:
            sl_out = []
            for i in range(st.dim):
                sl_out.append(slice(pos[i], None, self.iip[i]+1))
            # print("Initial Position:", pos)
            # print("Slice", sl_out)
            self.slices_out.append(tuple(sl_out.copy()))
Пример #5
0
 def __init__(self, residual_tolerance_dict, max_iteration_dict,
              *args, **kwargs):
     self.rtd = residual_tolerance_dict
     self.mid = max_iteration_dict
     if "res_comp_method" in kwargs.keys():
         self.compute_residual = kwargs["res_comp_method"]
         assert_is_callable(self.compute_residual,
                            "Residual computation method "
                            "should be callable ")
     else:
         self.compute_residual = self._standard_residual_computation
     super(ResidualErrorControl, self).__init__(args, kwargs)
Пример #6
0
    def __init__(self, stencil_list, level_in, level_out, *args, pre_assign=None, **kwargs):
        """init
        """
        super(InterpolationByStencilForLevels, self).__init__(*args, **kwargs)

        if pre_assign is None:
            # no pre assignment function
            self.pre_assign = lambda a, b: b
        else:
            assert_is_callable(pre_assign, "Pre assignment function is not callable")
            self.pre_assign = pre_assign

        # check if all parameters are fitting
        assert_is_instance(stencil_list, list)

        for st in stencil_list:
            assert_is_instance(st[0], Stencil, "that is not a stencil")
        self.stencil_list = stencil_list
        assert_is_instance(level_in, IMultigridLevel, "Not a IMultigridLevel")
        assert_is_instance(level_out, IMultigridLevel, "Not a IMultigridLevel")
        self.level_in = level_in
        self.level_out = level_out
        # increase in points for each direction
        self.iip = []
        for i in range(level_in.mid.ndim):
            self.iip.append((level_out.mid.shape[i]-1)/(level_in.mid.shape[i]-1) - 1)
            # print("in.shape[", i, "]: ", level_in.mid.shape[i])
            # print("out.shape[", i, "]: ", level_out.mid.shape[i])
            # print("iip[", i, "]: ", self.iip[-1])
            if (self.iip[-1] % 1) != 0:
                raise ValueError("The Levels do not match in direction " + str(i))
        # compute evaluable views with the positions and slices
        self.evaluable_views = []
        self.slices_out = []
        self.slices_in = []
        for st, pos in stencil_list:
            sl_out = []
            sl_in = []
            for i in range(st.dim):
                sl_out.append(slice(pos[i], None, self.iip[i]+1))
                if pos[i] == 0:
                    sl_in.append(slice(None, None))
                else:
                    sl_in.append(slice(0, -1))
            # print("Stencilcenter : ", st.center)
            # print("Stencilborder :", st.b)
            self.evaluable_views.append(level_in.evaluable_interpolation_view(st))
            # print("The view: \n", self.evaluable_views[-1])
            self.slices_out.append(tuple(sl_out.copy()))
            self.slices_in.append(tuple(sl_in.copy()))
Пример #7
0
    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)
Пример #8
0
    def init(self, problem, **kwargs):
        """Initializes the solver with a given problem and options.

        Parameters
        ----------
        problem : :py:class:`.IProblem`
            The problem this solver should solve.

        integrator : :py:class:`.IntegratorBase`
            Integrator to be used by this solver.

        threshold : :py:class:`.ThresholdCheck`
            *(optional)*
            see :py:attr:`.threshold`
        """
        self._problem = problem
        if 'integrator' in kwargs:
            assert_is_callable(kwargs['integrator'], message="Integrator must be instantiable.", checking_obj=self)
            self._integrator = kwargs['integrator']()
        if "threshold" in kwargs and isinstance(kwargs["threshold"], ThresholdCheck):
            self.threshold = kwargs["threshold"]
Пример #9
0
    def exact(self, time):
        """Evaluates given exact solution function at given time and with given time-dependent data.

        Parameters
        ----------
        time : :py:class:`float`
            Time point :math:`t`

        Returns
        -------
        exact_solution : :py:class:`numpy.ndarray`

        Raises
        ------
        ValueError :

            * if ``time`` is not a :py:class:`float`
            * if ``phi_of_time`` is not a :py:class:`numpy.ndarray`
            * if not exact function is given
        """
        assert_is_instance(time, float, descriptor="Time Point", checking_obj=self)
        assert_is_callable(self._exact_function, descriptor="Exact Function", checking_obj=self)
        return self._exact_function(time)
Пример #10
0
    def eval_f(self, u=None, function=None, space_tensor=None):
        """Evaluates the right hand side with the actual space tensor, and the current :math:`u`.
        """
        assert_condition(self._act_space_tensor is not None, ValueError,
                         message="A current space tensor is needed", checking_obj=self)

        if function is None:
            if u is None:
                return self.rhs_function_wrt_space(self._act_space_tensor)
            else:
                assert_is_instance(u, np.ndarray, "u is not an numpy array")
                assert_condition(u.shape == self._act_space_tensor[1].shape,
                                 "u has the wrong shape", self)
                return self.rhs_function_wrt_space(u, self._act_space_tensor)
        else:
            if u is None:
                assert_is_callable(function, "Function is not callable", self)
                return function(self._act_space_tensor)
            else:
                assert_is_instance(u, np.ndarray, "u is not an numpy array")
                assert_condition(u.shape == self._act_space_tensor[1].shape,
                                 "u has the wrong shape", self)
                return function(u, self._act_space_tensor)
Пример #11
0
 def rhs_function_wrt_time(self, function):
     assert_is_callable(function, descriptor="Function of the RHS w.r.t Time", checking_obj=self)
     self._rhs_function_wrt_time = function
Пример #12
0
 def exact_function(self, exact_function):
     assert_is_callable(exact_function, descriptor="Exact Function", checking_obj=self)
     self._exact_function = exact_function
Пример #13
0
    def __init__(self, mg_prob, stencil_form, *args, **kwargs):

        for keys in kwargs.keys():
            print(keys)
        # assert_condition(problem_is_multigrid_problem(mg_prob), ValueError, message="Not a multigrid Problem")
        assert_is_callable(stencil_form, "StencilForm has to be a function")
        self.mg_problem = mg_prob
        self.levels = []
        self.smoothers = []
        self.stencils = []
        self.rst_ops = []
        self.ipl_ops = []
        self.dim = kwargs["dim"]
        self.n_pre = kwargs.get("n_pre", 3)
        self.n_post = kwargs.get("n_post", 3)
        self.num_levels = kwargs.get("num_levels", 3)

        # append course level
        shape = kwargs["shape_coarse"]
        if kwargs.get("dim") == 1:
            self.levels.append(MultigridLevel1D(shape, self.mg_problem,
                                                max_borders=kwargs["max_borders"], role="CL"))
        elif kwargs.get("dim") == 2:
            self.levels.append(MultigridLevel2D(shape, self.mg_problem,
                                                max_borders=kwargs["max_borders"], role="CL"))
        #append course stencil
        self.stencils.append(Stencil(*stencil_form(self.levels[-1])))
        self.smoothers.append(DirectSolverSmoother(self.stencils[-1], self.levels[-1]))


        for i in range(kwargs["num_levels"]-1):

            if i == kwargs["num_levels"]-2:
                role = "FL"
            else:
                role = "ML"
            if kwargs.get("dim") == 1:
                shape = shape*2+1
                self.levels.append(MultigridLevel1D(shape, self.mg_problem,
                                                    max_borders=kwargs["max_borders"], role=role))
            elif kwargs.get("dim") == 2:
                shape = (shape[0]*2+1, shape[1]*2+1)
                self.levels.append(MultigridLevel2D(shape, self.mg_problem,
                                                    max_borders=kwargs["max_borders"], role=role))

            self.stencils.append(Stencil(*stencil_form(self.levels[-1])))
            # append smoother
            if kwargs["smoothing_type"] is "jacobi":
                omega = kwargs["smooth_opts"]["omega"]
                # l_plus = np.asarray([0, -2.0/omega, 0])
                # l_minus = np.asarray([1.0, -2.0*(1.0 - 1.0/omega), 1.0])
                l_plus = self.stencils[-1].l_plus_jacobi(omega)
                l_minus = self.stencils[-1].l_minus_jacobi(omega)
                self.smoothers.append(SplitSmoother(l_plus, l_minus, self.levels[-1]))
            elif kwargs["smoothing_type"] is "ilu":
                self.smoothers.append(ILUSmoother(self.stencils[-1], self.levels[-1], **kwargs["smooth_opts"]))
            else:
                raise ValueError("Wrong smoothing type")
            # append interpolation

            self.ipl_ops.append(kwargs["ipl_class"](self.levels[-2], self.levels[-1],
                                                    *kwargs.get("ipl_opts"), pre_assign=iadd))
            self.rst_ops.append(kwargs["rst_class"](self.levels[-1], self.levels[-2],
                                                    *kwargs.get("rst_opts")))
Пример #14
0
    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
Пример #15
0
    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)
Пример #16
0
 def rhs_function_wrt_space(self, function):
     assert_is_callable(function, descriptor="Function of RHS w.r.t. Space", checking_obj=self)
     self._rhs_function_wrt_space = function