Beispiel #1
0
    def extend(self, new_left, new_right, order = 0, new_parameters = None):
        """Extends the solution to a new domain using polynomial extrapolation.

        Parameters
        ----------
        new_left : float
            Location of the new left boundary point.
        new_right : float
            Location of the new right boundary point.
        order : int
            Order of the polynomial to use for extrapolation
        new_parameters : castable to floating point ndarray
            Value of new parameters to use.

        Returns
        -------
        extended : Solution
            Extended :class:`Solution`.
        """
        new_parameters = tools.preparg(new_parameters)
        if new_parameters is None:
            new_parameters = self._parameters

        npar = 0
        if not (self._parameters is None):
            npar = len(self._parameters)

        bvp_solverf.bvp.bvp_extend_wrap(node_in = self._solution.shape[0],
                                        npar_in = npar,
                                        leftbc_in = 1, # this value doesn't matter
                                        npts_in = len(self._mesh),
                                        info_in = self._successIndicator,
                                        mxnsub_in = 300, # nor does this
                                        x_in = tools.farg(self._mesh),
                                        y_in = tools.farg(self._solution),
                                        parameters_in = tools.farg(self._parameters),
                                        work_in = tools.farg(self._work),
                                        iwork_in = tools.farg(self._iwork),
                                        anew = new_left,
                                        bnew = new_right,
                                        order = order,
                                        p = tools.farg(new_parameters),
                                        max_num_subintervals = 300  # nor does this
                                        )

        result = self.from_arg_list(bvp_solverf.bvp)
        result._extended = True
        return result
Beispiel #2
0
    def __call__(self, points, eval_derivative = False):
        """Evaluates the approximate solution and optionally the first derivative at an array of points.

        Parameters
        ----------
        points : castable to floating point ndarray, shape (N)
            Array of points where the approximate solution and derivative should be evaluated.
        eval_derivative : logical
            Determines whether the first derivative should be returned.

        Returns
        -------
        S : floating point ndarray, shape(num_ODE,N)
            Vector containing the approximate solution evaluated at points. Variable index is first, point index is second.
        D : floating point ndarray, shape(num_ODE,N)
            Vector of the first derivative to the approximate solution evaluated at points. Variable index is first, point index is second (only returned if eval_derivative = True).

        Raises
        ------
        ValueError
            If the approximate solution cannot be evaluated.
        ValueError
            If the Solution is the result of an :meth:`extend` operation.
        """
        if self._successIndicator == -1:
            raise ValueError("Solution is the result of a failed run, cannot evaluate")

        if self._extended == True:
            raise ValueError("""this solution is the result of extending a previous solution and cannot be evaluated.
             If you really want to know what the solution looks like, look at .mesh and .solution""")

        # if any of the points are more than a certain tolerance outside of the bounds, something has gone wrong
        tol = 1e-12
        dist = tol * (self._mesh[-1] - self._mesh[0])
        if (points < self._mesh[0] - dist).any() or (points > self._mesh[-1] + dist).any():
            raise ValueError("some points are outside the bounds of the solution")

        npar = 0
        if not (self._parameters is None):
            npar = len(self._parameters)

        bvp_solverf.bvp.bvp_eval_wrap(eval_derivative = eval_derivative,
                        points = tools.farg(points),
                        node_in = self._solution.shape[0],
                        npar_in = npar,
                        leftbc_in = 1, # this value doesn't matter
                        npts_in = len(self._mesh),
                        info_in = self._successIndicator,
                        mxnsub_in = 300, # nor does this
                        x_in = tools.farg(self._mesh),
                        y_in = tools.farg(self._solution),
                        parameters_in = tools.farg(self._parameters),
                        work_in = tools.farg(self._work),
                        iwork_in = tools.farg(self._iwork))

        #would prefer not to copy arrays here
        # but the results end up getting screwed up when this function is called again for some other purpose
        # this is probably because the old arrays "deallocated" by Fortran

        if eval_derivative:
            return bvp_solverf.bvp.evaluated.copy(), bvp_solverf.bvp.evaluated_d.copy()
        else:
            return bvp_solverf.bvp.evaluated.copy()
Beispiel #3
0
def solve(bvp_problem,
          solution_guess,
          initial_mesh = None,
          parameter_guess = None,
          max_subintervals = 300,
          singular_term = None,
          tolerance = 1.0e-6,
          method = 4,
          trace = 0,
          error_on_fail = True):
    """Attempts to solve the supplied boundary value problem starting from the user supplied guess for the solution using BVP_SOLVER.

    Parameters
    ----------
    bvp_problem : :class:`ProblemDefinition`
        Defines the boundary value problem to be solved.
    solution_guess : :class:`Solution`, constant, array of values or function
        A guess for the solution.
    initial_mesh : castable to floating point ndarray
        Points on the x-axis to use for the supplied solution guess, default is 10 evenly spaced points. Must not be supplied if solution_guess is a :class:`Solution` object.
    parameter_guess : castable to floating point ndarray, shape (num_parameters)
        Guesses for the unknown parameters. Must not be supplied if solution_guess is a :class:`Solution` object.
    max_subintervals : int
        Maximum number of points on the mesh before an error is returned.
    singular_term : castable to floating point ndarray, shape(num_ODE, num_ODE)
        Matrix that defines the singular term for the problem if one exist.
    tolerance : positive float
        Tolerance for size of defect of approximate solution.
    method : {2, 4, 6}
        Order of Runge-Kutta to use.
    trace : {0, 1, 2}
        Indicates verbosity of output. 0 for no output, 1 for some output, 2 for full output.
    error_on_fail : logical
        Indicates whether an exception should be raised if solving fails.

    Returns
    -------
    sol : :class:`Solution`
        Approximate problem solution.

    Raises
    ------
    ValueError
        If bvp_problem failed validation in some way.
    ValueError
        If solving fails.
    """

    init_solution = 0

    if isinstance(solution_guess, Solution):
        if not (initial_mesh == None and
                parameter_guess == None):
            raise ValueError("values for initial mesh and parameter_guess must not be given if solution_guess is a Solution object")
        init_solution = solution_guess
    else:

        if initial_mesh == None:
            initial_mesh = numpy.linspace(bvp_problem.boundary_points[0],bvp_problem.boundary_points[1] , 10)

        # here we call one of the BVP_GUESS_i routines that make up BVP_INIT to set up the solution
        if ( not callable(solution_guess)):   # in this case the initial solution passed was not a function

            #try to cast the solution to an array
            solution_guess = numpy.array(solution_guess)

            # if the solution_guess is just an array the size of ODE
            if solution_guess.shape == (bvp_problem.num_ODE,) or (solution_guess.shape == () and bvp_problem.num_ODE == 1):

                bvp_solverf.bvp.guess_1_wrap(nparam_in = bvp_problem.num_parameters,
                                           leftbc_in = bvp_problem.num_left_boundary_conditions,
                                           x_in = tools.farg(initial_mesh),
                                           y_in = tools.farg(solution_guess),
                                           parameters_in = tools.farg(parameter_guess),
                                           mxnsub_in = max_subintervals,
                                           node_in = bvp_problem.num_ODE)

                init_solution = Solution.from_arg_list(bvp_solverf.bvp)

            else:

                tools.argShapeTest(solution_guess, (bvp_problem.num_ODE,initial_mesh.shape[0]), "solution guess")

                bvp_solverf.bvp.guess_2_wrap(nparam_in = bvp_problem.num_parameters,
                                             leftbc_in = bvp_problem.num_left_boundary_conditions,
                                             x_in = tools.farg(initial_mesh),
                                             y_in = tools.farg(solution_guess),
                                             parameters_in = tools.farg(parameter_guess),
                                             mxnsub_in = max_subintervals,
                                             node_in = bvp_problem.num_ODE)
                init_solution = Solution.from_arg_list(bvp_solverf.bvp)

        else:

            bvp_solverf.bvp.guess_3_wrap(node_in = bvp_problem.num_ODE,
                                           nparam_in = bvp_problem.num_parameters,
                                           leftbc_in = bvp_problem.num_left_boundary_conditions,
                                           x_in= tools.farg(initial_mesh),
                                           fcn = solution_guess,
                                           parameters_in = tools.farg(parameter_guess),
                                           mxnsub_in = max_subintervals)

            init_solution = Solution.from_arg_list(bvp_solverf.bvp)


    if not (method == 2 or method == 4 or method == 6 ):
        raise ValueError ("method must be either 2, 4 or 6 but got " + str(method) )

    if (tolerance < 0):
        raise ValueError("tolerance must be nonnegative")

    singular = not (singular_term is None)

    # check to see if the singular term is of the right size
    singular_term = tools.preparg(singular_term)
    if singular and not (singular_term.shape == (bvp_problem.num_ODE, bvp_problem.num_ODE)):
        raise ValueError("singular_term has the wrong shape/size. Expected: " +
                         (bvp_problem.num_ODE, bvp_problem.num_ODE)+
                         " but got :" +
                          singular_term.shape)

    # test the problem specifications with the initial solution
    bvp_problem.test(init_solution)
                                    
                                
                    
    bvp_solverf.bvp.bvp_solver_wrap(node_in = bvp_problem.num_ODE,
                                    npar_in = bvp_problem.num_parameters,
                                    leftbc_in = bvp_problem.num_left_boundary_conditions,
                                    npts_in = len(init_solution.mesh),
                                    info_in = init_solution.successIndicator,
                                    mxnsub_in = max_subintervals,
                                    x_in = tools.farg(init_solution.mesh),
                                    y_in = tools.farg(init_solution.solution),
                                    parameters_in = tools.farg(init_solution.parameters),
                                    work_in = tools.farg(init_solution.work),
                                    iwork_in = tools.farg(init_solution.iwork),
                                    fsub = bvp_problem._function,
                                    fsubp = bvp_problem._functionp,
                                    bcsub = bvp_problem._boundary_conditions,
                                    bcsubp = bvp_problem._boundary_conditionsp,

                                    singular = singular,

                                    hasdfdy = bvp_problem.has_function_derivative,
                                    dfdy = bvp_problem._function_derivative,
                                    dfdyp = bvp_problem._function_derivativep,

                                    hasdbcdy = bvp_problem.has_boundary_conditions_derivative,
                                    dbcdy = bvp_problem._boundary_conditions_derivative,
                                    dbcdyp = bvp_problem._boundary_conditions_derivativep,
                                    #optional arguments
                                    method = method,
                                    tol = tolerance,

                                    trace = trace,
                                    # we never want the actual program to shut down from the fortran side, we should throw an error
                                    stop_on_fail = False,

                                    singularterm = tools.farg(singular_term))

    calculatedSolution = Solution.from_arg_list(bvp_solverf.bvp)

    # check to see if there was a problem with the solution
    if error_on_fail and calculatedSolution._successIndicator == -1:
        raise ValueError("Boundary value problem solving failed. Run with trace = 1 or 2 for more information.")

    return calculatedSolution
Beispiel #4
0
def solve(bvp_problem,
          solution_guess,
          initial_mesh = None,
          parameter_guess = None,
          max_subintervals = 300,
          singular_term = None,
          tolerance = 1.0e-6,
          method = 4,
          trace = 0,
          error_on_fail = True):
    """Attempts to solve the supplied boundary value problem starting from the user supplied guess for the solution using BVP_SOLVER.

    Parameters
    ----------
    bvp_problem : :class:`ProblemDefinition`
        Defines the boundary value problem to be solved.
    solution_guess : :class:`Solution`, constant, array of values or function
        A guess for the solution.
    initial_mesh : castable to floating point ndarray
        Points on the x-axis to use for the supplied solution guess, default is 10 evenly spaced points. Must not be supplied if solution_guess is a :class:`Solution` object.
    parameter_guess : castable to floating point ndarray, shape (num_parameters)
        Guesses for the unknown parameters. Must not be supplied if solution_guess is a :class:`Solution` object.
    max_subintervals : int
        Maximum number of points on the mesh before an error is returned.
    singular_term : castable to floating point ndarray, shape(num_ODE, num_ODE)
        Matrix that defines the singular term for the problem if one exist.
    tolerance : positive float
        Tolerance for size of defect of approximate solution.
    method : {2, 4, 6}
        Order of Runge-Kutta to use.
    trace : {0, 1, 2}
        Indicates verbosity of output. 0 for no output, 1 for some output, 2 for full output.
    error_on_fail : logical
        Indicates whether an exception should be raised if solving fails.

    Returns
    -------
    sol : :class:`Solution`
        Approximate problem solution.

    Raises
    ------
    ValueError
        If bvp_problem failed validation in some way.
    ValueError
        If solving fails.
    """

    init_solution = 0

    if isinstance(solution_guess, Solution):
        if not (initial_mesh == None and
                parameter_guess == None):
            raise ValueError("values for initial mesh and parameter_guess must not be given if solution_guess is a Solution object")
        init_solution = solution_guess
    else:

        if initial_mesh == None:
            initial_mesh = numpy.linspace(bvp_problem.boundary_points[0],bvp_problem.boundary_points[1] , 10)

        # here we call one of the BVP_GUESS_i routines that make up BVP_INIT to set up the solution
        if ( not callable(solution_guess)):   # in this case the initial solution passed was not a function

            #try to cast the solution to an array
            solution_guess = numpy.array(solution_guess)

            # if the solution_guess is just an array the size of ODE
            if solution_guess.shape == (bvp_problem.num_ODE,) or (solution_guess.shape == () and bvp_problem.num_ODE == 1):

                bvp_solverf.bvp.guess_1_wrap(nparam_in = bvp_problem.num_parameters,
                                           leftbc_in = bvp_problem.num_left_boundary_conditions,
                                           x_in = tools.farg(initial_mesh),
                                           y_in = tools.farg(solution_guess),
                                           parameters_in = tools.farg(parameter_guess),
                                           mxnsub_in = max_subintervals,
                                           node_in = bvp_problem.num_ODE)

                init_solution = Solution.from_arg_list(bvp_solverf.bvp)

            else:

                tools.argShapeTest(solution_guess, (bvp_problem.num_ODE,initial_mesh.shape[0]), "solution guess")

                bvp_solverf.bvp.guess_2_wrap(nparam_in = bvp_problem.num_parameters,
                                             leftbc_in = bvp_problem.num_left_boundary_conditions,
                                             x_in = tools.farg(initial_mesh),
                                             y_in = tools.farg(solution_guess),
                                             parameters_in = tools.farg(parameter_guess),
                                             mxnsub_in = max_subintervals,
                                             node_in = bvp_problem.num_ODE)
                init_solution = Solution.from_arg_list(bvp_solverf.bvp)

        else:
            y_in = numpy.zeros((bvp_problem.num_ODE,1))

            bvp_solverf.bvp.guess_1_wrap(nparam_in = bvp_problem.num_parameters,
                                           leftbc_in = bvp_problem.num_left_boundary_conditions,
                                           x_in = tools.farg(initial_mesh),
                                           y_in = y_in,
                                           parameters_in = tools.farg(parameter_guess),
                                           mxnsub_in = max_subintervals,
                                           node_in = bvp_problem.num_ODE)

            init_solution = Solution.from_arg_list(bvp_solverf.bvp)
            for i,v in enumerate(init_solution._mesh):
                init_solution._solution[:, i] = solution_guess(v)


    if not (method == 2 or method == 4 or method == 6 ):
        raise ValueError ("method must be either 2, 4 or 6 but got " + str(method) )

    if (tolerance < 0):
        raise ValueError("tolerance must be nonnegative")

    singular = not (singular_term is None)

    # check to see if the singular term is of the right size
    singular_term = tools.preparg(singular_term)
    if singular and not (singular_term.shape == (bvp_problem.num_ODE, bvp_problem.num_ODE)):
        raise ValueError("singular_term has the wrong shape/size. Expected: " +
                         (bvp_problem.num_ODE, bvp_problem.num_ODE)+
                         " but got :" +
                          singular_term.shape)

    # test the problem specifications with the initial solution
    bvp_problem.test(init_solution)
                                    
                                
                    
    bvp_solverf.bvp.bvp_solver_wrap(node_in = bvp_problem.num_ODE,
                                    npar_in = bvp_problem.num_parameters,
                                    leftbc_in = bvp_problem.num_left_boundary_conditions,
                                    npts_in = len(init_solution.mesh),
                                    info_in = init_solution.successIndicator,
                                    mxnsub_in = max_subintervals,
                                    x_in = tools.farg(init_solution.mesh),
                                    y_in = tools.farg(init_solution.solution),
                                    parameters_in = tools.farg(init_solution.parameters),
                                    work_in = tools.farg(init_solution.work),
                                    iwork_in = tools.farg(init_solution.iwork),
                                    fsub = bvp_problem._function,
                                    fsubp = bvp_problem._functionp,
                                    bcsub = bvp_problem._boundary_conditions,
                                    bcsubp = bvp_problem._boundary_conditionsp,

                                    singular = singular,

                                    hasdfdy = bvp_problem.has_function_derivative,
                                    dfdy = bvp_problem._function_derivative,
                                    dfdyp = bvp_problem._function_derivativep,

                                    hasdbcdy = bvp_problem.has_boundary_conditions_derivative,
                                    dbcdy = bvp_problem._boundary_conditions_derivative,
                                    dbcdyp = bvp_problem._boundary_conditions_derivativep,
                                    #optional arguments
                                    method = method,
                                    tol = tolerance,

                                    trace = trace,
                                    # we never want the actual program to shut down from the fortran side, we should throw an error
                                    stop_on_fail = False,

                                    singularterm = tools.farg(singular_term))

    calculatedSolution = Solution.from_arg_list(bvp_solverf.bvp)

    # check to see if there was a problem with the solution
    if error_on_fail and calculatedSolution._successIndicator == -1:
        raise ValueError("Boundary value problem solving failed. Run with trace = 1 or 2 for more information.")

    return calculatedSolution