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
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
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
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