Solving a 4th order BVP using python - python

I am trying to solve a 4th order ODE
EI*(d4y/dx4) = -k*y
which is a version of an ODE for a foundation on soft soil.
My system of first-order equations is the following
y1' = y2
y2' = y3
y3' = y4
y4' = -k*y1
and the BCs
y3(0) = y3(L) = 0
y4(0) = -F/(EI)
y4(L) = 0
and my code
from scipy.integrate import solve_bvp
import numpy as np
F = 300 #kN
EI = 20000 #kNm2
D = 1 #m
M_E = 45000 #kN/m2
k = 1.4*M_E/D #kN/m3
L = 10 #m
x = np.linspace(0,L,101)
p = np.array([k,EI,F])
print(p)
def fun(x, y, p):
k = p[0]
EI = p[1]
return np.vstack((y[1],y[2],y[3],-k*y[0]/EI))
def bc(ya, yb, p):
F = p[2]
EI = p[1]
return np.array([ya[2], yb[2], ya[3]+F/EI, yb[3]])
y_a = np.zeros((4, x.size))
from scipy.integrate import solve_bvp
res_a = solve_bvp(fun, bc, x, y_a, p)
I get the following error:
#ValueError: 'bc' return is expected to have shape (7,), but actually has (4,).
Could you help me understand what I am doing wrong here and what the error means?
Thanks

So, after playing around with the code, I managed to make it work. The solution would be:
from scipy.integrate import solve_bvp
import numpy as np
F = 100 #kN
D = 1 #m
M_E = 50 #MN/m2
E_cm = 33600 #MN/m2
L = 10 #m
nodes = 40
x = np.linspace(0,L,nodes)
k = 1.4*M_E/D #kN/m3
EI = E_cm*0.25*np.pi*(D*0.5)**4 #kNm2
c =k*D/EI
def fun(x, y):
return np.vstack((y[1],y[2],y[3],-c*y[0]))
def bc(ya, yb):
return np.array([ya[2], yb[2], ya[3]+F/EI, yb[3]])
y_a = np.zeros((4, x.size))
res_a = solve_bvp(fun, bc, x, y_a)
y = res_a.sol(x)[0]*10**3
theta = res_a.sol(x)[1]*10**3
M = EI*res_a.sol(x)[2]
V = EI*res_a.sol(x)[3]
I hope this helps anyone who is struggling with implementing an ODE of orders higher than 2.

Related

Runge Kutta 4th order Python

I am trying to solve this equation using Runge Kutta 4th order:
applying d2Q/dt2=F(y,x,v) and dQ/dt=u Q=y in my program.
I try to run the code but i get this error:
Traceback (most recent call last):
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 28, in <module>
k1 = F(y, u, x) #(x, v, t)
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\asdasda.py", line 13, in F
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
OverflowError: (34, 'Result too large')
I tried using the decimal library but I still couldnt make it work properly.I might have not used it properly tho.
My code is this one:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t=0.93
def F(y, u, x):
return ((Vo/L -(R0/L)*u -(R1/L)*u**3 - y*(1/L*C)))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = 0.0
u = Vo/L
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
m3 = h*(u + 0.5*k2)
k3 = h*F(y+0.5*m2, u+0.5*k2, x+0.5*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + 2*m2 + 2*m3 + m4)/6
u += (k1 + 2*k2 + 2*k3 + k4)/6
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
I expected to get the plots of u and y against t.
Turns out I messed up with the equations I was using for Runge Kutta
The correct code is the following:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
#parameters
R0 = 200
R1 = 250
L = 15
h = 0.002
Vo=1000
C=4.2*10**(-6)
t0=0
#dz/dz
def G(x,y,z):
return Vo/L -(R0/L)*z -(R1/L)*z**3 - y/(L*C)
#dy/dx
def F(x,y,z):
return z
t = np.arange(t0, 0.93, h)
x = np.zeros(len(t))
y = np.zeros(len(t))
z = np.zeros(len(t))
y[0] = 0.0
z[0] = 0
for i in range(1, len(t)):
k0=h*F(x[i-1],y[i-1],z[i-1])
l0=h*G(x[i-1],y[i-1],z[i-1])
k1=h*F(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
l1=h*G(x[i-1]+h*0.5,y[i-1]+k0*0.5,z[i-1]+l0*0.5)
k2=h*F(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
l2=h*G(x[i-1]+h*0.5,y[i-1]+k1*0.5,z[i-1]+l1*0.5)
k3=h*F(x[i-1]+h,y[i-1]+k2,z[i-1]+l2)
l3 = h * G(x[i - 1] + h, y[i - 1] + k2, z[i - 1] + l2)
y[i]=y[i-1]+(k0+2*k1+2*k2+k3)/6
z[i] = z[i - 1] + (l0 + 2 * l1 + 2 * l2 + l3) / 6
Q=y
I=z
plot(t, Q)
show()
plot(t, I)
show()
If I may draw your attention to these 4 lines
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = h*(u + 0.5*k1)
k2 = (h*F(y+0.5*m1, u+0.5*k1, x+0.5*h))
You should note a fundamental structural difference between the first two lines and the second pair of lines.
You need to multiply with the step size h also in the first pair.
The next problem is the step size and the cubic term. It contributes a term of size 3*(R1/L)*u^2 ~ 50*u^2 to the Lipschitz constant. In the original IVP per the question with u=Vo/L ~ 70 this term is of size 2.5e+5. To compensate only that term to stay in the stability region of the method, the step size has to be smaller 1e-5.
In the corrected initial conditions with u=0 at the start the velocity u remains below 0.001 so the cubic term does not determine stability, this is now governed by the last term contributing a Lipschitz term of 1/sqrt(L*C) ~ 125. The step size for stability is now 0.02, with 0.002 one can expect quantitatively useful results.
You can use decimal libary for more precision (handle more digits), but it's kind of annoying every value should be the same class (decimal.Decimal).
For example:
import numpy as np
from math import pi
from numpy import arange
from matplotlib.pyplot import plot, show
# Import decimal.Decimal as D
import decimal
from decimal import Decimal as D
# Precision
decimal.getcontext().prec = 10_000_000
#parameters
# Every value should be D class (decimal.Decimal class)
R0 = D(200)
R1 = D(250)
L = D(15)
h = D(0.002)
Vo = D(1000)
C = D(4.2*10**(-6))
t = D(0.93)
def F(y, u, x):
# Decomposed for use D
a = D(Vo/L)
b = D(-(R0/L)*u)
c = D(-(R1/L)*u**D(3))
d = D(-y*(D(1)/L*C))
return ((a + b + c + d ))
xpoints = arange(0,t,h)
ypoints = []
upoints = []
y = D(0.0)
u = D(Vo/L)
for x in xpoints:
ypoints.append(y)
upoints.append(u)
m1 = u
k1 = F(y, u, x) #(x, v, t)
m2 = (h*(u + D(0.5)*k1))
k2 = (h*F(y+D(0.5)*m1, u+D(0.5)*k1, x+D(0.5)*h))
m3 = h*(u + D(0.5)*k2)
k3 = h*F(y+D(0.5)*m2, u+D(0.5)*k2, x+D(0.5)*h)
m4 = h*(u + k3)
k4 = h*F(y+m3, u+k3, x+h)
y += (m1 + D(2)*m2 + D(2)*m3 + m4)/D(6)
u += (k1 + D(2)*k2 + D(2)*k3 + k4)/D(6)
plot(xpoints, upoints)
show()
plot(xpoints, ypoints)
show()
But even with ten million of precision I still get an overflow error. Check the components of the formula, their values are way too high. You can increase precision for handle them, but you'll notice it takes time to calculate them.
Problem implementation using scipy.integrate.odeint and scipy.integrate.solve_ivp.
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint, solve_ivp
# Input data initial conditions
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
t_span = np.linspace(ti,tf,N)
r0 = np.array([Q0,u0])
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Systems of First Order Equations
# This function is used with odeint, as specified in the documentation for scipy.integrate.odeint
def f(r,t,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# This function is used in our 4Order Runge-Kutta implementation and in scipy.integrate.solve_ivp
def F(t,r,R0,R1,L,C,V0):
Q,u = r
ode1 = u
ode2 = -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
return np.array([ode1,ode2])
# Resolution with oedint
sol_1 = odeint(f,r0,t_span,args=(R0,R1,L,C,V0))
sol_2 = solve_ivp(fun=F,t_span=(ti,tf), y0=r0, method='LSODA',args=(R0,R1,L,C,V0))
Q_odeint, u_odeint = sol_1[:,0], sol_1[:,1]
Q_solve_ivp, u_solve_ivp = sol_2.y[0,:], sol_2.y[1,:]
# Figures
plt.figure(figsize=[30.0,10.0])
plt.subplot(3,1,1)
plt.grid(color = 'red',linestyle='--',linewidth=0.4)
plt.plot(t_span,Q_odeint,'r',t_span,u_odeint,'b')
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,2)
plt.plot(sol_2.t,Q_solve_ivp,'g',sol_2.t,u_solve_ivp,'y')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel('Q(t), u(t)')
plt.subplot(3,1,3)
plt.plot(Q_solve_ivp,u_solve_ivp,'green')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('Q(t)')
plt.ylabel('u(t)')
plt.show()
Runge-Kutta 4th
# Code development of Runge-Kutta 4 Order
# Parameters
R0 = 200
R1 = 250
L = 15
C = 4.2*10**(-6)
V0 = 1000
# Input data initial conditions #
ti = 0.0
tf = 0.5
N = 100000
h = (tf-ti)/N
# Initial conditions
u0 = 0.0
Q0 = 0.0
# First order ordinary differential equations
def f1(t,Q,u):
return u
def f2(t,Q,u):
return -((R0/L)*u)-((R1/L)*u**3)-((1/(L*C))*Q)+(V0/L)
t = np.zeros(N); Q = np.zeros(N); u = np.zeros(N)
t[0] = ti
Q[0] = Q0
u[0] = u0
for i in range(0,N-1,1):
k1 = h*f1(t[i],Q[i],u[i])
l1 = h*f2(t[i],Q[i],u[i])
k2 = h*f1(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
l2 = h*f2(t[i]+(h/2),Q[i]+(k1/2),u[i]+(l1/2))
k3 = h*f1(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
l3 = h*f2(t[i]+(h/2),Q[i]+(k2/2),u[i]+(l2/2))
k4 = h*f1(t[i]+h,Q[i]+k3,u[i]+l3)
l4 = h*f2(t[i]+h,Q[i]+k3,u[i]+l3)
Q[i+1] = Q[i] + ((k1+2*k2+2*k3+k4)/6)
u[i+1] = u[i] + ((l1+2*l2+2*l3+l4)/6)
t[i+1] = t[i] + h
plt.figure(figsize=[20.0,10.0])
plt.subplot(1,2,1)
plt.plot(t,Q_solve_ivp,'r',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{Odeint}$, $Q(t)_{RK4}$')
plt.subplot(1,2,2)
plt.plot(t,Q_solve_ivp,'g',t,Q_odeint,'y',t,Q,'b')
plt.grid(color = 'yellow',linestyle='--',linewidth=0.4)
plt.xlabel('t(s)')
plt.ylabel(r'$Q(t)_{solve_ivp}$, $Q(t)_{RK4}$')

Minimize function via scipy with the one dependent parameter

I need to optimize my function with 3 variables: r, poly, eps.
Parameter poly depends on the r parameter. r and eps are independent but have some boundaries.
How to write the constraints for the minimization function in the right way?
import numpy as np
from sklearn.metrics import mean_squared_error as mse
from rbf.pde.fd import weights
from scipy.optimize import minimize
from sklearn.neighbors import KDTree as KDT
nx = 101
nz = nx
dx = 1
dz = dx
x,z = np.arange(0, nx)*dx, np.arange(0, nz)*dz
X, Z = np.meshgrid(x, z)
grid = np.column_stack([X.ravel(), Z.ravel()])
nn = len(grid)
boundary_index = np.argwhere((grid[:,0]==x[0]) | (grid[:,0]==x[-1]) | (grid[:,1]==z[0]) | (grid[:,1]==z[-1])).ravel()
checking = np.array([True]*nn); checking[boundary_index]=False
FDM_coefs = [-4, 1, 1, 1, 1]
def weight_matrix2(x, p, r ,diffs,
phi='phs3',
order=None,
eps=1.0):
neighbors = KDT(p).query_radius(x, r)
coefficients = np.array([weights(x[i], p[neighbors[i]], diffs = diffs, phi=phi, order=order, eps=eps) for i in range(len(p))])
return neighbors, coefficients
def loss(args):
r, poly, Eps = args[0], args[1], args[2]
nb, coefs = weight_matrix2(
x=grid,
p=grid,
r=r,
diffs=[(2, 0), (0, 2)],
phi='phs4',
order=poly,
eps=Eps)
a = np.array([np.sort([i]) for i in coefs[checking].copy()])
a = np.squeeze(a)
error = np.empty_like(a)
error = [mse(a[i], FDM_coefs, squared=False) for i in range(len(a))]
return np.mean(error)
initial_guess = [1, 1, 10.**(-5)]
bnds = ((1,10),(1,10),(10.**(-10), 10.**(5)))
def constraint2(args): #Return possible poly values
r = args[0]
n = max(np.unique([len(i) for i in KDT(grid).query_radius(grid, r)[checking==True]]))
mp = (np.sqrt(8*n+1)-3)//2
return np.arange(1, mp+1)
#constraint 2 means which values poly variable can take == np.arange(1, mp+1)
con2 = {'type':'eq', 'fun':constraint2}
result = minimize(loss, initial_guess, method='SLSQP',options={'maxiter':100, 'disp':True},
bounds=bnds, constraints=con2)

Solving a complex BVP problem with scipy solve_bvp

I have a complex BVP problem (almost Shrodinger equation). My code works but the solution is completely wrong or just equal to zero. What am I doing wrong?
I also have obtained right solution by Maple, there was no problem. Also I don't understand why there is no problem with plot when it has to be complex-valued function.
import numpy as np
from scipy.integrate import odeint
from scipy.integrate import solve_bvp as bvp
import matplotlib.pyplot as plt
mp = 938.2720813
mn = 939.5654133
mu = (mn + mp)/4
h2m = hbar**2/(2*mu)
V0 = 20
Rv = 1.5
Q0 = 1.5
Rq = 4.5
EIm = 0.3
ERe = 1
V = lambda x : -V0*np.exp(-x/Rv)
Q = lambda x : -Q0*np.exp(-x/Rq)
def fun(x, y):
return np.vstack((y[1], -( Q(x)/ h2m ) - ((ERe + 1j * EIm) *y[0]/ h2m ) + V(x)*y[0]/h2m - (2/y[0])* y[1]))
def bc(ya, yb):
return np.array([ya[0], yb[0]])
x = np.linspace(0, 1000, 10000)
y_a = np.zeros((2, x.size), dtype=np.complex)
# print(x.size)
i = 0
while i < x.size - 1:
i = i + 1
y_a[0, i] = 1000* 1j
y_a[1, i] = 1j
from scipy.integrate import solve_bvp
res_a = solve_bvp(fun, bc, x, y_a)
x_plot = np.linspace(0, 1000, 10000)
y_plot_a = res_a.sol(x_plot)[0]
import matplotlib.pyplot as plt
plt.plot(x_plot, y_plot_a, label='y_a')
plt.legend()
plt.xlabel("x")
plt.ylabel("y")
plt.show()
Upd: I fixed a mistake in the equation.
Result is still wrong. But there is another error - division by zero. How to avoid it? If I choose x = np.linspace(0.1, 1000, 10000) for example it doesn't help.

Linear regression(Python)-getting different results for different algorithm

I implemented gradient descent for linear regression in Python for a data read from csv file. below is the code:
import numpy as np
import scipy.optimize as op
from sklearn import preprocessing
import matplotlib.pyplot as plt
from matplotlib import style
def CostFunc(theta,x,y):
m,n = x.shape;
theta = theta.reshape((n,1));
#y = y.reshape((m,1));
#x=x.reshape((m,1))
J = 0.5*sum(np.square((X.dot(theta)-y)))[0]/m;
return J;
def gradientDescent(X, y, theta, alpha, num_iters):
m,n = X.shape;
initial_iter = num_iters
J = np.zeros((num_iters,1));
while num_iters > 0:
theta = theta - (alpha*np.sum(((X.dot(theta)-y)*X),axis=0)/m).reshape((n,1))
J[initial_iter-num_iters][0]=CostFunc(theta,X,y)
num_iters=num_iters-1
return theta,J;
data = np.loadtxt(open("ex1data2.txt","rb"),delimiter=",",skiprows=1)
nr,nc = data.shape
X=data[:,0:nc - 1]
X=preprocessing.scale(X)
X=np.insert(X,0,1,axis=1)
y= data[:,[nc - 1]]
m , n = X.shape;
#initial_theta = np.zeros(len(n));
initial_theta = np.zeros((n,1));
theta,J=gradientDescent(X,y,initial_theta,0.01,400)
J=J.reshape(400)
count=0
grp=np.zeros((400,2));
for value in J:
grp[count][0] = count+1
grp[count][1] = value
count = count+1
plt.plot(grp)
plt.legend(loc=4)
plt.xlabel('iter')
plt.ylabel('cost')
plt.show()
print(theta)
optimal_theta = Result.x;
I ran 400 iterations and got a decreasing plot of cost as followsiteration vs cost plot
Then I used one of the custom algorithms on the same data with the following code
import numpy as np
import scipy.optimize as op
from sklearn import preprocessing
def Sigmoid(z):
return 1/(1 + np.exp(-z));
def Gradient(theta,x,y):
m , n = x.shape
theta = theta.reshape((n,1));
#y = y.reshape((m,1))
#x=x.reshape((m,1))
#sigmoid_x_theta = Sigmoid(x.dot(theta));
grad = (np.sum(((X.dot(theta)-y)*X),axis=0)/m);
return grad.flatten();
def CostFunc(theta,x,y):
m,n = x.shape;
theta = theta.reshape((n,1));
#y = y.reshape((m,1));
#x=x.reshape((m,1))
J = 0.5*sum(np.square((X.dot(theta)-y)))[0]/m;
return J;
data = np.loadtxt(open("ex1data2.txt","rb"),delimiter=",",skiprows=1)
nr,nc = data.shape
X=data[:,0:nc - 1]
X=preprocessing.scale(X)
X=np.insert(X,0,1,axis=1)
y= data[:,[nc - 1]]
m , n = X.shape;
#initial_theta = np.zeros(len(n));
initial_theta = np.zeros((n,1));
Result = op.minimize(fun = CostFunc,
x0 = initial_theta,
args = (X, y),
method = 'TNC',
jac = Gradient);
print("====================")
print(Result)
#optimal_theta = Result.x;
The theta values of the first case were
[[ 333032.07465084] [ 100130.7408761 ] [ 3699.66611303]]
While from the second I get the following output
fun: 2066502781.7118049
jac: array([ -6.45345806e-11, 7.84261236e-10, -2.42370452e-10])
message: 'Local minimum reached (|pg| ~= 0)'
nfev: 27
nit: 13
status: 0
success: True
x: array([ 339119.45652174, 110248.92165868, -6226.22670554])
I am assuming x: array([ 339119.45652174, 110248.92165868, -6226.22670554]) to be the theta value.
Why am I getting the values different for 2 cases?

python - different array length along interpolation axis?

I am trying to use the Python interpolation function to get the value y for a given x but I am getting the error "raise ValueError("x and y arrays must be equal in length along along interpolation axis" even though my arrays have both equal size and shape (according to what I get when I use .shape in my code). I am quite new to programming so I don't know how to check what else could be different in my arrays. Here is my code:
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
After adding in the required imports, I've been unable to duplicate your error with version 2.7.12. What python version are you running?
import numpy as np
from scipy.interpolate import interp1d
s = []
def slowroll(y, t):
phi, dphi, a = y
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
da = h*a
ddphi = -3.*h*dphi - phi
return [dphi,ddphi,da]
phi_ini = 18.
dphi_ini = -0.1
init_y = [phi_ini,dphi_ini,1.]
h_ini =np.sqrt(1/3. * (1/2. * dphi_ini**2. + 1/2.*phi_ini**2.))
t=np.linspace(0.,20.,100.)
from scipy.integrate import odeint
sol = odeint(slowroll, init_y, t)
phi = sol[:,0]
dphi = sol[:,1]
a=sol[:,2]
n=np.log(a)
h = np.sqrt(1/3. * (1/2. * dphi**2 + 1/2.*phi**2))
s.extend(a*h)
x = np.asarray(s)
y = np.asarray(t)
F = interp1d(y, x, kind='cubic')
print F(7.34858263)
Output:
2.11688518961e+20

Categories