Differential equation of forced harmonic oscillator is given as Mx''+Lx'+(w^2)x=F(t). Here F(t) is a source term. To solve this problem I wrote a code where I define the differential equation in a function 'diff'. I wrote another function 'generate_pulse' that gives the F(t).
Then I use 'odeint', which solves the differential equation by calling the 'diff' function along with other parameters. Now I don't get any error message if I put F=0 inside the 'diff' function (i.e., ignore any F(t) term). Please have a look inside the 'diff' function:
F=0 #No error detected if I put F=0 here. Comment out this line to see the error
Once I keep the F(t), I get an error message 'ValueError: setting an array element with a sequence.'
How to solve the problem?
Code:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import math
import itertools
def diff(y, t,param):
M=param[0]
L=param[1]
w2=param[2]
F=param[3]
F=0 #No error detected if I put F=0 here. Comment out this line to see the error
x,v = y
dydt = [v, (F-L*v - w2*x)/M]
return dydt
def generate_pulse(t,Amp,init_delay,rtime,PW):
L=len(t)
t0=t[0:math.ceil(init_delay*L/100)]
t1=t[len(t0):len(t0)+math.ceil(rtime*L/100)]
t2=t[len(t0)+len(t1):len(t0)+len(t1)+math.ceil(PW*L/100)]
t3=t[len(t0)+len(t1)+len(t2):len(t0)+len(t1)+len(t2)+2*math.ceil(rtime*L/100)]
t4=t[len(t0)+len(t1)+len(t2)+len(t3):len(t0)+len(t1)+len(t2)+len(t3)+math.ceil(PW*L/100)]
t5=t[len(t0)+len(t1)+len(t2)+len(t3)+len(t4):len(t0)+len(t1)+len(t2)+len(t3)+len(t4)+math.ceil(rtime*L/100)]
t6=t[len(t0)+len(t1)+len(t2)+len(t3)+len(t4)+len(t5):]
s0=0*t0
s1=(Amp/(t1[-1]-t1[0]))*(t1-t1[0])
s2=np.full((1,len(t2)),Amp)
s2=list(itertools.chain.from_iterable(s2)) #The 'tuple' is converted into array
s3=-Amp/(t1[-1]-t1[0])*(t3-t3[0])+Amp
s4=np.full((1,len(t4)),-Amp)
s4=list(itertools.chain.from_iterable(s4)) #The 'tuple' is converted into array
s5=(Amp/(t5[-1]-t5[0]))*(t5-t5[0])-Amp
s6=np.full((1,len(t6)),0)
s6=list(itertools.chain.from_iterable(s6)) #The 'tuple' is converted into array
s=[s0,s1,s2,s3,s4,s5,s6]
s=list(itertools.chain.from_iterable(s))
return s
###############################################################################
# Main code from here
t = np.linspace(0, 30, 200)
y0 = [- 10, 0.0]
M=5
L = 1
w2 = 15.0
Amp=5
init_delay=10
rtime=10
PW=10
F=generate_pulse(t,Amp,init_delay,rtime,PW)
Param=([M,L,w2,F],) #Making the 'Param' a tuple. Because args of odeint takes tuple as argument.
sol = odeint(diff, y0, t, args=Param)
plt.plot(t, sol[:, 0], 'b', label='x(t)')
plt.plot(t,F,'g',label='Force(t)')
plt.legend(loc='best')
plt.show()
You get the error because the value of F that you pass is the array that you generated.
Use the interpolation functions of numpy or scipy to make an actual function out of the arrays. Then evaluate that function at time t. Or directly implement the forcing term as a piecewise defined function of the scalar t.
Also note that the list of sampling times you give in t to odeint has (almost) nothing to do with the times at which odeint calls the ODE function diff. If you want to control that you would have to implement your own fixed-step method, probably not Runge-Kutta but some multi-step method.
Related
I am trying to find the global minimum of the Sum of Squared Differences which contains five different parameters x[0], x[1], x[2], x{3], x[4] coming from an affine transformation of data. Since I have a lot of data to compare the different_evolution approach was speeded up with the use of numba as you can see in the code part below. (More details to the code can be found here: How to speed up differential_evolution to find the minimum of Sum of Squared Errors with five variables)
My problem now is, that I get different values for my parameters for running the same Code several times. This problem is also described in Differential evolution algorithm different results for different runs but I use the scipy.optimize package to import differential_evolution instead of mystic. I tried to use mystic instead but then I get de following error message:
TypeError: CPUDispatcher(<function tempsum3 at 0x000002AA043999D0>) is not a Python function
My question now is, if there is any appproach that really gives me back the global minimum of my function? An approach using the powell-method with the minimize command gives back worse values for my parameters than differential_evolution (gets stuck in local minmia even faster). And if there is no other way than different_evolution, how can I be sure that I really have the global Minimum at some point? And in case the mystic approach is expedient, how do i get it going?
I would be thankful for any kind of advice,
Here's my function to minimize with the use of numba to speed it up and the mystic pacakge, that gives me back an error message:
from math import floor
import matplotlib.pyplot as plt
import numpy as np
import mystic as my
from mystic.solvers import diffev
import numba as nb
w1=5 #A1
h1=3
w2=8#A2
h2=5
hw1=w1*h1
hw2=w2*h2
A1=np.ones((h1,w1))
A2=np.ones((h2,w2))
for n1 in np.arange(2,4):
A1[1][n1]=1000
for n2 in np.arange(5,7):
A2[3][n2]=1000
A2[4][n2]=1000
#Norm the raw data
maxA1=np.max(A1)
A1=1/maxA1*A1
#Norm the raw data
maxA2=np.max(A2)
A2=1/maxA2*A2
fig, axes = plt.subplots(1, 2)
axes[0].imshow(A2)
axes[0].set_title('original-A2')
axes[1].imshow(A1)
axes[1].set_title('shifted-A1')
plt.show()
#nb.njit
def xy_to_pixelvalue(x,y,dataArray): #getting the values to the normed pixels
height=dataArray.shape[0]
width=dataArray.shape[1]
xcoord=floor((x+1)/2*(width-1))
ycoord=floor((y+1)/2*(height-1))
if -1 <=x<=1 and -1<=y<=1:
return dataArray[ycoord][xcoord]
else:
return(0)
#norming pixel coordinates
A1x=np.linspace(-1,1,w1)
A1y=np.linspace(-1,1,h1)
A2x=np.linspace(-1,1,w2)
A2y=np.linspace(-1,1,h2)
#normed coordinates of A2 in a matrix
Ap2=np.zeros((h2,w2,2))
for i in np.arange(0,h2):
for j in np.arange(0,w2):
Ap2[i][j]=(A2x[j],A2y[i])
#defining a vector with the coordinates of A2
d=[]
cdata=Ap2[:,:,0:2]
for i in np.arange(0,h2):
for j in np.arange(0,w2):
d.append((cdata[i][j][0],cdata[i][j][1]))
d=np.asarray(d)
coora2=d.transpose()
coora2=np.array(coora2, dtype=np.float64)
#nb.njit('(float64[::1],)')
def tempsum3(x):
tempsum=0
for l in np.arange(0,hw2):
tempsum += (xy_to_pixelvalue(np.cos(np.radians(x[2]))*x[0]*coora2[0][l]-np.sin(np.radians(x[2]))*x[1]*coora2[1][l]+x[3],np.sin(np.radians(x[2]))*x[0]*coora2[0][l]+np.cos(np.radians(x[2]))*x[1]*coora2[1][l]+x[4],A1)-xy_to_pixelvalue(coora2[0][l],coora2[1][l],A2))**2
return tempsum
x0 = np.array([1,1,-0.5,0,0])
bounds = [(0.1,5),(0.1,5),(-1,2),(-4,4),(-4,4)]
result = diffev(tempsum3, x0, npop = 5*15, bounds = bounds, ftol = 1e-11, gtol = 3500, maxiter = 1024**3, maxfun = 1024**3, full_output=True, scale = 0.8)
print(result)
(SciPy optimisation: Newton-CG vs BFGS vs L-BFGS)
Consider the following area: D = [-5.10]x[0.15]. This task, given below, must be performed in
this domain.And executed in Jupyter notebook
Draw N = 100 random points uniformly distributed over D. For each point, run a local minimization of f using scipy.optimize.minimize with the following methods:
CG,BFGS,Newton-CG,L-BFGS-B. For this task, you will have to write two other functions, one that returns the Jacobian matrix of f and one that returns the Hessian matrix of f . Store the answers in an array with shape N x 6, each row of which has the following data:
(x1, y1,x2, y2,v,c),
where (x1, y1) and (x2, y2) are respectively the starting and the final point of the optimization, while v is the final value of f . The final element of the row c is code of the used method, according to this correspondence: CG:1, BFGS:2, Newton-CG:3, L-BFGS-B:4
I got this code
import numpy as np
from scipy import optimize
from scipy.optimize import minimize
#%from numdifftools import Jacobian, Hessian
#method
mmm='CG'
#mmm='BFGS'
#mmm='Newton-CG'
#mmm='L-BFGS-B'
a=1
b=5.1/(4*np.pi**2)
c=5/np.pi
r=6
s=10
t=1/(8*np.pi)
a1=-5
b1=10
c1=0
d1=15
def f_brain(xx):
aa=a*(xx[1]-b*xx[0]**2+c*xx[0]-r)**2+s*(1-t)*np.cos(xx[0])+s
return aa
def fun_der(xx):
aa1=2*a*(xx[1]-b*(xx[0]**2)+c*xx[0]-r)*(-2*b*xx[0]+c)-s*(1-t)*np.sin(xx[0])
aa2=2*a*(xx[1]-b*(xx[0]**2)+c*xx[0]-r)
return np.array([aa1,aa2])
def fun_hess(xx):
b11=2*a*(c-2*b*xx[0])**2-2*b*(xx[1]-b*xx[0]**2+c*xx[1]-r)-s*(1-t)*np.cos(xx[0])
b12=2*a*(c-2*b*xx[0])
b21=2*a*(c-2*b*xx[0])
b22=2*a
return np.array([[b11,b12],[b21,b22]])
# importing libraries
x0=np.array([a1,c1])
x_bound = (a1,b1)
y_bound = (c1,d1)
result = optimize.minimize(f_brain, x0=x0, method=mmm,
options={'maxiter':100}, jac=fun_der(x0).all(), hess=fun_hess(x0).all(),
bounds=(x_bound,y_bound))
print(result)
print("Method is "+mmm+" and the Minimum occurs at: ", result['x'])
x00=np.float64(result['x'])
print("Jacobian", fun_der(x00))
print("Hessian", fun_hess(x00))
Defining a integral of multi variable function as a second function python
I am using python to integrate a multivariable function over only one of the variables (it is a function of x and theta and I am integrating over theta from 0 to 2*pi, hence the result is a function of x). I have attempted the following:
import numpy as np
import scipy.integrate as inte
d=10.0
xvals=np.linspace(-d,d,1000)
def aIntegrand(theta,x):
return 1/(2*np.pi)*np.sin(2*np.pi*x*np.sin(theta)/d)**2
def A(x):
return (inte.quad(aIntegrand,0,2*np.pi,args=(x,))[0])**(1/2)
plt.plot(xvals,A(xvals))
plt.xlabel("x")
plt.ylabel("A(x)")
plt.show()
and I get the following error:
TypeError: only size-1 arrays can be converted to Python scalars
I assume this is because the result of the quad integrator is an array with two elements and python doesn't like defining the function based on an indexed array? That is a complete guess of the problem though. If anyone knows how I can fix this and could let me know, that'd be great :)
A second attempt
I have managed to get the plot of the integral successfully using the following code:
import numpy as np
import scipy.integrate as inte
import matplotlib.pyplot as plt
d=10.0
xvals=np.linspace(-d,d,1000)
thetavals=np.linspace(0.0,2*np.pi,1000)
def aIntegrand(theta,x):
return 1/(2*np.pi)*np.sin(2*np.pi*x*np.sin(theta)/d)**2
def A(x):
result=np.zeros(len(x))
for i in range(len(x)):
result[i]=(inte.quad(aIntegrand,0,2*np.pi,args=(x[i],))[0])**(1/2)
return result
def f(x,theta):
return x**2* np.sin(theta)
plt.plot(xvals,A(xvals))
plt.xlabel("x")
plt.ylabel("A(x)")
plt.show()
But this does not give A(x) as a function, due to the way I have defined it, it requires an array form input. I need the function to be of the same form as aIntegrand, where when given parameters returns a single value & so the function can be integrated repeatedly.
I do not think that what you seek exists within Scipy. However, you have at least two alternatives.
First, you can create an interpolator using interp1d. This means that the range of x values you initially give determines the range for which you will be able to call the interpolator. This interpolator can then be called for any value of x in this interval.
You can perform the second integral even though you do not have a callable. The function simps only requires values and their location to provide an estimate of the integration process. Otherwise, you can perform the double integration in one call with dblquad.
First note that your integral can be calculated analytically. It is
0.5 * (1 - J0(4 * pi * x / d))
where J0 is the Bessel function of the first kind.
Second, you could use quadpy (one of my projects); it has fully vectorized computation.
import numpy as np
import quadpy
import matplotlib.pyplot as plt
import scipy.special
d = 10.0
x = np.linspace(-d, d, 1000)
def aIntegrand(theta):
return (
1
/ (2 * np.pi)
* np.sin(2 * np.pi * np.multiply.outer(x, np.sin(theta)) / d) ** 2
)
Ax2, err = quadpy.quad(aIntegrand, 0, 2 * np.pi)
Ax = np.sqrt(Ax2)
plt.plot(x, Ax, label="quadpy")
plt.xlabel("x")
plt.ylabel("A(x)")
plt.plot(x, np.sqrt(0.5 * (1 - scipy.special.jv(0, 4*np.pi*x/d))), label="bessel")
# plt.show()
plt.savefig("out.png", transparent=True, bbox_inches="tight")
I want to solve this differential equation:
y′′+2y′+2y=cos(2x) with initial conditions:
y(1)=2,y′(2)=0.5
y′(1)=1,y′(2)=0.8
y(1)=0,y(2)=1
and it's code is:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
def dU_dx(U, x):
return [U[1], -2*U[1] - 2*U[0] + np.cos(2*x)]
U0 = [1,0]
xs = np.linspace(0, 10, 200)
Us = odeint(dU_dx, U0, xs)
ys = Us[:,0]
plt.xlabel("x")
plt.ylabel("y")
plt.title("Damped harmonic oscillator")
plt.plot(xs,ys);
how can I fulfill it?
Your initial conditions are not, as they give values at two different points. These are all boundary conditions.
def bc1(u1,u2): return [u1[0]-2.0,u2[1]-0.5]
def bc2(u1,u2): return [u1[1]-1.0,u2[1]-0.8]
def bc3(u1,u2): return [u1[0]-0.0,u2[0]-1.0]
You need a BVP solver to solve these boundary value problems.
You can either make your own solver using the shooting method, in case 1 as
def shoot(b): return odeint(dU_dx,[2,b],[1,2])[-1,1]-0.5
b = fsolve(shoot,0)
T = linspace(1,2,N)
U = odeint(dU_dx,[2,b],T)
or use the secant method instead of scipy.optimize.fsolve, as the problem is linear this should converge in 1, at most 2 steps.
Or you can use the scipy.integrate.solve_bvp solver (which is perhaps newer than the question?). Your task is similar to the documented examples. Note that the argument order in the ODE function is switched in all other solvers, even in odeint you can give the option tfirst=True.
def dudx(x,u): return [u[1], np.cos(2*x)-2*(u[1]+u[0])]
Solutions generated with solve_bvp, the nodes are the automatically generated subdivision of the integration interval, their density tells how "non-flat" the ODE is in that region.
xplot=np.linspace(1,2,161)
for k,bc in enumerate([bc1,bc2,bc3]):
res = solve_bvp(dudx, bc, [1.0,2.0], [[0,0],[0,0]], tol=1e-5)
print res.message
l,=plt.plot(res.x,res.y[0],'x')
c = l.get_color()
plt.plot(xplot, res.sol(xplot)[0],c=c, label="%d."%(k+1))
Solutions generated using the shooting method using the initial values at x=0 as unknown parameters to then obtain the solution trajectories for the interval [0,3].
x = np.linspace(0,3,301)
for k,bc in enumerate([bc1,bc2,bc3]):
def shoot(u0): u = odeint(dudx,u0,[0,1,2],tfirst=True); return bc(u[1],u[2])
u0 = fsolve(shoot,[0,0])
u = odeint(dudx,u0,x,tfirst=True);
l, = plt.plot(x, u[:,0], label="%d."%(k+1))
c = l.get_color()
plt.plot(x[::100],u[::100,0],'x',c=c)
You can use the scipy.integrate.ode function this is similar to scipy.integrate.odeint but allows a jac parameter which is df/dy or in the case of your given ODE df/dx
I am trying to plot an integration of a special (eg. Bessel) function and my minimal code is the following.
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
import scipy.special as sp
from scipy.special import jn
#x = np.arange(0.0, 10.0, 0.1)
U = np.linspace(0,10,1000)
#Delta = U**2
#Delta = U-4+8*integrate.quad(lambda x: sp.jv(1,x)/(x*(1.0+np.exp(U*x*0.5))), 0, 100)
Delta = U-4+8*integrate.quad(lambda x: jn(1,x)/(x*(1.0+np.exp(U*x*0.5))), 0.1, 1000)
plt.plot(U,Delta)
plt.xlabel('U')
plt.ylabel('$\Delta$')
plt.show()
However, this gives me several an error messages saying quadpack.error: Supplied function does not return a valid float whereas the function gets easily plotted in Mathematica. Do Python's Bessel's functions have limitations?
I have used this documentation for my plotting.
It is difficult to provide an answer that solves the problem before understanding what exactly you are trying to do. However, let me list a number of issues and provide an example that may not achieve what you are trying to do but at least it will provide a path forward.
Because your lambda function multiplies x by an array U, it returns an array instead of a number. A function that needs to be integrated should return a single number. You could fix this, for example, by replacing U by u:
f = lambda x, u: jn(1,x)/(x*(1.0+np.exp(u*x*0.5)))
Make Delta a function of u AND make quad pass additional argument u to f (defined in the previous point) AND extract only the value of the integral from the returned tuple from quad (quad returns a tuple of several values: the integral, the error, etc.):
Delta = lambda u: -4+8*integrate.quad(f, 0.1, 1000, args=(u,))[0]
Compute Delta for each u:
deltas = np.array(map(Delta, U))
plot the data:
plt.plot(U, deltas)