Differential equation of kepler orbit numerical solution in python? - python

So, I'm trying to write a code that solves the (what we called) differential equation of an orbit in the kepler potential V(r)=-1/r
when you do the math you get a differential equation that looks like this:
d^2u/d(fi)^2 + u - m/M^2=0
where u=1/r
and we are ultimately looking for r(fi)
and now i tried to solve it using the numerical method, first i said du/dfi=y
then definig a function (i took some arbitrary M and m)
def func(y,fi):
m=4
M=5
return [y[1],m/M^2-y[0]]$
and imported from scipy.integrate import odeint
and then put in
ts = np.linspace(0,15,150)
ys = odeint(func, y0, ts)
now this gets me an array of 150 arrays of two numbers
and i don't really understand what dodes the first number mean and what does the second number mean is is
ys=[fi,u(fi)]
or something else?

The state for your order one system is [value, derivative]. The result of the integration is a list of state pairs of the same type.

Related

Is there a way to integrate and get an array or a function instead of all the area under the curve?

I want to integrate the following equation:
d^2[Ψ(z)] / dz^2 = A * ρ(z)
Where Ψ (unknown) and ρ (known) are 1-D arrays and A is a constant.
I have already performed a Taylor expansion, i.e.
d^2[Ψ(z_0)] / dz^2 = [Ψ(z0+Δz) - 2Ψ(z0) + Ψ(z0-Δz)] / [Δz^2]
And successfully solve it by building a matrice.
Now, I would like to know if there is a Python (preferably) or Matlab function that can solve this function without having to do a Taylor expansion.
I have tried numpy.trapz and scipy.integrate.quad, but it seems that these functions only return the area under the curve, i.e. a number, and I am interested to get an array or a function (solving for Ψ).
What you want to do is to solve the differential equation. Because it is a second order differential equation, you should modify your function to make it a system of first order ODEs. So you have to create a function like this:
Assuming ρ=rho
def f(z, y):
return np.array([y[1], A*rho(z)])
where y is a vector containing Ψ in the first position and its derivative in the second position. Then, f returns a vector containing the first and second derivatives of Ψ.
Once done that, you can use scipy.integrate.solve_ivp to solve the problem:
scipy.integrate.solve_ivp(f, [z_start, z_final], y0, method='RK45', z_eval)
where y0 are the initial conditions of Ψ (the value of Ψ and its derivative at z_start). z_eval is the points where you want to store the solution. The solution will be an array containing the values of Ψ and its derivative.
You could integrate rho twice using an indefinite integral . e.g.
import numpy as np
x=np.arange(0,3.1,0.1)
rho = x**3 # replace your rho here....
indef_integral1 = [np.trapz(rho[0:i],x[0:i]) for i in range(2,len(rho))]
indef_integral2 = [np.trapz(indef_integral1[0:i],x[0:i]) for i in range(2,len(indef_integral1))]

ValueError: diff requires input that is at least one dimensional

I studying how to solve differential equations in Python with odeint and for test, I try to solve the following ODE (the following example came from of https://apmonitor.com/pdc/index.php/Main/SolveDifferentialEquations):
# first import the necessary libraries
import numpy as np
from scipy.integrate import odeint
# function that returns dy/dt
def model(y,t):
k = 0.3
dydt = -k*y
return dydt
#Initial condition
y0 = 5.0
# Time points
t = np.linspace(0,20)
# Solve ODE
def y(t):
return odeint(model,y0,t)
So if I plot the results with matplotlib, or more simply, give the command print(y(t)) then this work perfectly! But if I try compute the value of the function for a fixed value of time, for instance, t1 = t[2] ( = 0.8163 ) so I get the error
t1 = t[2]
print(y(t1))
ValueError("diff requires input that is at least one dimensional")
why I only can compute the value for y(t) for a interval t = np.linspace(0,20) but not for a number in this interval? There is some manner to fix this?
Thank you very much.
The odeint function solves you differential equation numerically. To do that you need to specify the points where you want your solution to be evaluated. These points also influence the accuracy of the solution. Generally, the more points you give to odeint the better the result (when solving the same time interval).
This means that there is no way for odeint to know what accuracy you want if you only supply a single time at which you want to evaluate the function. Instead you always need to supply a range of times (like you did with np.linspace). odeint then returns the value of the solution at all these times.
y(t) is an array of values of your solution and the third value in the array corresponds to the solution at the third time in t:
The solution evaluated at t[0] is y(t)[0] = y0
The solution evaluated at t[1] is y(t)[1]
The solution evaluated at t[2] is y(t)[2]
...
So instead of
print(y(t[2]))
you need to use
print(y(t)[2])

Solving non-linear coupled differential equations in python

I am working on simulation of a system that contains coupled differential equations. My main aim is to solve the mass balance in steady condition and feed the solution of steady state as initial guess for the dynamic simulation.
There are basically three state variables Ss,Xs and Xbh. The rate equations look like this:
r1=µH(Ss/(Ks+Ss))(So/(Koh+So))Xbh+Kh(
(Xs⁄Xbh)/(Xs⁄Xbh+Kx))(So/(Koh+So))Xbh
r2=(1-fp)bH*Xbh-Kh( (Xs⁄Xbh)/(Xs⁄Xbh+Kx))(So/(Koh+So))Xbh
r3=µH(Ss/(Ks+Ss))(So/(Koh+So))Xbh-bH*Xbh
And the main differential equations derived from mole balance for CSTR are:
dSs/dt = Q(Ss_in-Ss)+r1*V
dXs/dt= Q(Xs_in-Xs)+r2*V
dXbh/dt= Q(Xbh_in-Xbh)+r2*V
Here is my code till now:
import numpy as np
from scipy.optimize import fsolve
parameter=dict()
parameter['u_h']=6.0
parameter['k_oh']=0.20
parameter['k_s']=20.0
parameter['k_h']=3.0
parameter['k_x']=0.03
parameter['Y_h']=0.67
parameter['f_p']=0.08
parameter['b_h']=0.62
Bulk_DO=2.0 #mg/L
#influent components:
infcomp=[56.53,182.9,16.625] #mgCOD/l
Q=684000 #L/hr
V=1040000 #l
def steady(z,*args):
Ss=z[0]
Xs=z[1]
Xbh=z[2]
def monod(My_S,My_K):
return My_S/(My_S+My_K)
#Conversion rates
#Conversion of Ss
r1=((-1/parameter['Y_h'])*parameter['u_h']*monod(Ss,parameter['k_s'])\
+parameter['k_h']*monod(Xs/Xbh,parameter['k_x'])*monod(Bulk_DO,parameter['k_oh']))\
*Xbh*monod(Bulk_DO,parameter['k_oh'])
#Conversion of Xs
r2=((1-parameter['f_p'])*parameter['b_h']-parameter['k_h']*monod(Xs/Xbh,parameter['k_x']))*Xbh
#Conversion of Xbh
r3=(parameter['u_h']*monod(Ss,parameter['k_s'])*monod(Bulk_DO,parameter['k_oh'])-parameter['b_h'])*Xbh
f=np.zeros(3)
f[0]=Q*(infcomp[0]-Ss)+r1*V
f[1]=Q*(infcomp[1]-Xs)+r2*V
f[2]=Q*(infcomp[2]-Xbh)+r3*V
return f
initial_guess=(0.1,0.1,0.1)
soln=fsolve(steady,initial_guess,args=parameter)
print (soln)
How can I plot steady condition like this?
steady state plot
The solution is also not what I want since the equations implies reduction in Ss and Xs and increase of Xbh values with time. Also one solution has negative value which is practically impossible.
Any suggestions would be highly appreciated. Thanks in advance !!
This is a solution to getting negative values for your solution: instead of using fsolve, use least_squares, which allows you to set bounds to the possible values.
In the top, import:
from scipy.optimize import least_squares
And replace the fsolve statement with:
soln = least_squares(steady, initial_guess, bounds=[(0,0,0),(np.inf,np.inf,np.inf)], args=parameter)

Performing a double integral over a matrix of limits

I have recently been learning how to perform double integrals in python. This is what I am using:
myint = dblquad(lambda xa,xb: np.exp(-(xa-xb)**2),-np.inf,x1,lambda x: -np.inf, lambda x: x2)
and for testing purposes I have chosen x1 and x2 to be say 5 and 10. This seems to work.
But in reality, my x1 = [1,2,3,4,5] and x2 = [5,6,7,8,9] and I want the double integral to be performed over every combination of x1 and x2 i.e. a matrix. I could do this with 2 for loops I guess, but I thought there might be a better way.
So my question is just - how do I perform a double integration over a matrix of limits.
Thank you.
edit:
I got the following warning:
UserWarning: The maximum number of subdivisions (50) has been achieved.
If increasing the limit yields no improvement it is advised to analyze
the integrand in order to determine the difficulties. If the position of a
local difficulty can be determined (singularity, discontinuity) one will
probably gain from splitting up the interval and calling the integrator
on the subranges. Perhaps a special-purpose integrator should be used.
Does this mean that it doesn't converge? I don't really understand the message.
When I plot:
y = exp(-(x-5)^2)
for example, it just looks like a gaussian curve, so there is no problem integrating over that right? Is the problem because of the double integral?
Thank you.
edit:
Ah, I see. Thanks Raman Shah, I understand the problem now.
Using itertools you can create an iterator of limits to walk over. This essentially is a double loop, but for more extensible as you can have an arbitrary number of inputs with itertools.product and you don't store all the limits at once:
import numpy as np
from scipy.integrate import dblquad
import itertools
f = lambda xa,xb: np.exp(-(xa-xb)**2)
intg = lambda (x1,x2): dblquad(f,-np.inf,x1,
lambda x:-np.inf,
lambda x:x2)
X1 = np.arange(1,6)
X2 = np.arange(5,10)
for limit in itertools.product(X1,X2):
print limit, intg(limit)
If you need more speed, you can look into the multiprocessing module for parallel computation since each process is independent.
Why not use pythons zip function to feed exactly the values from each tuple that you want treated as inputs to your double integral, and then use map/apply to operate on those discrete pairs

On ordinary differential equations (ODE) and optimization, in Python

I want to solve this kind of problem:
dy/dt = 0.01*y*(1-y), find t when y = 0.8 (0<t<3000)
I've tried the ode function in Python, but it can only calculate y when t is given.
So are there any simple ways to solve this problem in Python?
PS: This function is just a simple example. My real problem is so complex that can't be solve analytically. So I want to know how to solve it numerically. And I think this problem is more like an optimization problem:
Objective function y(t) = 0.8, Subject to dy/dt = 0.01*y*(1-y), and 0<t<3000
PPS: My real problem is:
objective function: F(t) = 0.85,
subject to: F(t) = sqrt(x(t)^2+y(t)^2+z(t)^2),
x''(t) = (1/F(t)-1)*250*x(t),
y''(t) = (1/F(t)-1)*250*y(t),
z''(t) = (1/F(t)-1)*250*z(t)-10,
x(0) = 0, y(0) = 0, z(0) = 0.7,
x'(0) = 0.1, y'(0) = 1.5, z'(0) = 0,
0<t<5
This differential equation can be solved analytically quite easily:
dy/dt = 0.01 * y * (1-y)
rearrange to gather y and t terms on opposite sides
100 dt = 1/(y * (1-y)) dy
The lhs integrates trivially to 100 * t, rhs is slightly more complicated. We can always write a product of two quotients as a sum of the two quotients * some constants:
1/(y * (1-y)) = A/y + B/(1-y)
The values for A and B can be worked out by putting the rhs on the same denominator and comparing constant and first order y terms on both sides. In this case it is simple, A=B=1. Thus we have to integrate
1/y + 1/(1-y) dy
The first term integrates to ln(y), the second term can be integrated with a change of variables u = 1-y to -ln(1-y). Our integrated equation therefor looks like:
100 * t + C = ln(y) - ln(1-y)
not forgetting the constant of integration (it is convenient to write it on the lhs here). We can combine the two logarithm terms:
100 * t + C = ln( y / (1-y) )
In order to solve t for an exact value of y, we first need to work out the value of C. We do this using the initial conditions. It is clear that if y starts at 1, dy/dt = 0 and the value of y never changes. Thus plug in the values for y and t at the beginning
100 * 0 + C = ln( y(0) / (1 - y(0) )
This will give a value for C (assuming y is not 0 or 1) and then use y=0.8 to get a value for t. Note that because of the logarithm and the factor 100 multiplying t y will reach 0.8 within a relatively short range of t values, unless the initial value of y is incredibly small. It is of course also straightforward to rearrange the equation above to express y in terms of t, then you can plot the function as well.
Edit: Numerical integration
For a more complexed ODE which cannot be solved analytically, you will have to try numerically. Initially we only know the value of the function at zero time y(0) (we have to know at least that in order to uniquely define the trajectory of the function), and how to evaluate the gradient. The idea of numerical integration is that we can use our knowledge of the gradient (which tells us how the function is changing) to work out what the value of the function will be in the vicinity of our starting point. The simplest way to do this is Euler integration:
y(dt) = y(0) + dy/dt * dt
Euler integration assumes that the gradient is constant between t=0 and t=dt. Once y(dt) is known, the gradient can be calculated there also and in turn used to calculate y(2 * dt) and so on, gradually building up the complete trajectory of the function. If you are looking for a particular target value, just wait until the trajectory goes past that value, then interpolate between the last two positions to get the precise t.
The problem with Euler integration (and with all other numerical integration methods) is that its results are only accurate when its assumptions are valid. Because the gradient is not constant between pairs of time points, a certain amount of error will arise for each integration step, which over time will build up until the answer is completely inaccurate. In order to improve the quality of the integration, it is necessary to use more sophisticated approximations to the gradient. Check out for example the Runge-Kutta methods, which are a family of integrators which remove progressive orders of error term at the cost of increased computation time. If your function is differentiable, knowing the second or even third derivatives can also be used to reduce the integration error.
Fortunately of course, somebody else has done the hard work here, and you don't have to worry too much about solving problems like numerical stability or have an in depth understanding of all the details (although understanding roughly what is going on helps a lot). Check out http://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.ode.html#scipy.integrate.ode for an example of an integrator class which you should be able to use straightaway. For instance
from scipy.integrate import ode
def deriv(t, y):
return 0.01 * y * (1 - y)
my_integrator = ode(deriv)
my_integrator.set_initial_value(0.5)
t = 0.1 # start with a small value of time
while t < 3000:
y = my_integrator.integrate(t)
if y > 0.8:
print "y(%f) = %f" % (t, y)
break
t += 0.1
This code will print out the first t value when y passes 0.8 (or nothing if it never reaches 0.8). If you want a more accurate value of t, keep the y of the previous t as well and interpolate between them.
As an addition to Krastanov`s answer:
Aside of PyDSTool there are other packages, like Pysundials and Assimulo which provide bindings to the solver IDA from Sundials. This solver has root finding capabilites.
Use scipy.integrate.odeint to handle your integration, and analyse the results afterward.
import numpy as np
from scipy.integrate import odeint
ts = np.arange(0,3000,1) # time series - start, stop, step
def rhs(y,t):
return 0.01*y*(1-y)
y0 = np.array([1]) # initial value
ys = odeint(rhs,y0,ts)
Then analyse the numpy array ys to find your answer (dimensions of array ts matches ys). (This may not work first time because I am constructing from memory).
This might involve using the scipy interpolate function for the ys array, such that you get a result at time t.
EDIT: I see that you wish to solve a spring in 3D. This should be fine with the above method; Odeint on the scipy website has examples for systems such as coupled springs that can be solved for, and these could be extended.
What you are asking for is a ODE integrator with root finding capabilities. They exist and the low-level code for such integrators is supplied with scipy, but they have not yet been wrapped in python bindings.
For more information see this mailing list post that provides a few alternatives: http://mail.scipy.org/pipermail/scipy-user/2010-March/024890.html
You can use the following example implementation which uses backtracking (hence it is not optimal as it is a bolt-on addition to an integrator that does not have root finding on its own): https://github.com/scipy/scipy/pull/4904/files

Categories