Example #1
0
    def __init__(self,
                 mesh,   #the current mesh
                 solution,   #the current solution
                 parameters = None,
                 work = None,
                 iwork = None,
                 yerror = None,
                 successIndicator = None,
                 extended = False):
        """
        """
        self._mesh = tools.preparg(mesh)
        self._solution = tools.preparg(solution)

        self._parameters = tools.preparg(parameters)
        self._work = tools.preparg(work)
        self._iwork = tools.preparg(iwork)
        self._yerror = yerror
        self._successIndicator = successIndicator
        self._extended = extended
Example #2
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
    def __init__(self,
                 num_ODE,
                 num_parameters,
                 num_left_boundary_conditions,
                 boundary_points,
                 function,
                 boundary_conditions,
                 function_derivative = None,
                 boundary_conditions_derivative = None):
        """
        Parameters
        ----------
        num_ODE : int
            Number of first order ordinary differential equations in the problem.
        num_parameters : int
            Number of unknown parameters in the problem.
        num_left_boundary_conditions : int
            Number of boundary conditions enforced on the left boundary.
        boundary_points : arraylike, shape(2)
            Array that defines the two boundary points on the x axis.
        function : function (see definition below)
            A function which calculates the value of the ODE equations.

                function (X, Y[, P]):

                    Parameters:
                        X : float
                            scalar value of x at which to evaluate the ODEs

                        Y : ndarray, shape(num_ODE)
                            current value of all variables

                        P : ndarray, shape(num_parameters)
                            value of all unknown parameters (only included if num_parameters > 0)
                    Returns:
                        ODE : ndarray, shape(num_ODE)
                            array of all ODEs

        boundary_conditions : function (see definition below)
            A function which calculates the difference between the boundary conditions and the actual variables currently calculated.

                boundary_conditions(YA, YB[, P]):

                    Parameters:
                        YA : ndarray, shape(num_ODE)
                            value of all variables at the left boundary

                        YB : ndarray, shape(num_ODE)
                            value of all variables at the right boundary

                        P : ndarray, shape(num_parameters)
                            value of all unknown parameters (only used if num_parameters > 0)

                    Returns:
                        BCA : ndarray, shape(num_left_boundary_conditions)
                            difference between the boundary condition and variables at the left boundary

                        BCB : ndarray, shape(num_ODE + num_parameters - num_left_boundary_conditions)
                            array of the difference between the boundary condition and variables at the right boundary

        function_derivative : optional function (see definition below)
            A function which returns the partial derivatives of the function argument.

                function (X, Y[, P]):

                    Parameters:
                        X : float
                            scalar value of x at which to evaluate the ODEs

                        Y : ndarray, shape(num_ODE)
                            current value of all variables

                        P : ndarray, shape(num_parameters)
                            value of all unknown parameters (only included if num_parameters > 0)
                    Returns:
                        dODE : ndarray, shape(num_ODE, num_ODE)
                            array of partial derivative of all ODEs with respect to all variables; index of ODEs is first, index of variables is second

                        dOdP : ndarray, shape(num_ODE, num_parameters)
                            array of partial derivative of all ODEs with respect to all unknown parameters; index of ODEs is first, index of parameters is second must not be returned if the problem does not include unknown parameters


        boundary_conditions_drivative : optional function (see definition below)
            A function which returns the partial derivatives of the boundary_conditions argument.
                boundary_conditions(YA, YB[, P]):

                    Parameters:
                        YA : ndarray, shape(num_ODE)
                            value of all variables at the left boundary

                        YB : ndarray, shape(num_ODE)
                            value of all variables at the right boundary

                        P : ndarray, shape(num_parameters)
                            value of all unknown parameters (only used if num_parameters > 0)

                    Returns:
                        dBCA : ndarray, shape(num_left_boundary_conditions, num_ODE)
                            partial derivatives of the difference between the left boundary condition and the actual variables at the left boundary; boundary condition index is first and variable index is second

                        dBCB : ndarray, shape(num_ODE + num_parameters - num_left_boundary_conditions, num_ODE)
                            partial derivatives of the difference between the right boundary condition and the actual variables at the right boundary; boundary condition index is first and variable index is second

                        dBPA : ndarray, shape(num_left_boundary_conditions, num_parameters)
                            partial derivatives of the difference between the left boundary condition and the unknown parameters; boundary condition index is first and parameter index is second

                        dBPB : ndarray, shape(num_ODE + num_parameters - num_left_boundary_conditions, num_parameters)
                            partial derivatives of the difference between the right boundary condition and the unknown parameters; boundary condition index is first and parameter index is second

        """

        self._num_ODE = num_ODE
        self._num_parameters = num_parameters
        self._num_left_boundary_conditions = num_left_boundary_conditions
        self._boundary_points = tools.preparg(boundary_points)
        self._function = function
        self._function_store = function

        self._boundary_conditions =boundary_conditions
        self._boundary_conditions_store =boundary_conditions

        self._function_derivative_store = function_derivative
        self._function_derivative = function_derivative

        self._boundary_conditions_derivative_store = boundary_conditions_derivative
        self._boundary_conditions_derivative = boundary_conditions_derivative


        #figure out whether the user has supplied derivatives for the function and boundary conditions
        self.has_function_derivative = not self._function_derivative is None
        self.has_boundary_conditions_derivative = not self._boundary_conditions_derivative is None

        if self._num_parameters == 0:

            # if don't have unknown parameters then give all the arguments for callbacks with parameter arguments dummy functions
            self._functionp = fp_dummy
            self._boundary_conditionsp = bcp_dummy
            self._function_derivativep = fderivep_dummy
            self._boundary_conditions_derivativep = bcderivep_dummy

            # also assign dummy arguments to optional derivatives if they were not supplied
            if self._function_derivative is None:
                self._function_derivative = f_dummy

            if self._boundary_conditions_derivative is None:
                self._boundary_conditions_derivative = bcderive_dummy
        else:

            #also assign all the supplied arguments to the callback arguments with parameter arguments in them
            self._functionp = function
            self._boundary_conditionsp = self._boundary_conditions
            self._function_derivativep = self._function_derivative
            self._boundary_conditions_derivativep = self._boundary_conditions_derivative

            # if have unknown parameters then give all the arguments for callbacks without parameter arguments dummy functions
            self._function = f_dummy
            self._boundary_conditions = bc_dummy
            self._function_derivative = fderive_dummy
            self._boundary_conditions_derivative = bcderive_dummy

            # give dummy arguments in those cases when no derivative arugment is supplied
            if self._function_derivativep is None:
                self._function_derivativep = fp_dummy


            if self._boundary_conditions_derivativep is None:
                self._boundary_conditions_derivativep = bcderivep_dummy
Example #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:

            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
Example #5
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