我正在尝试使用基于RK5和Newton方法的射击方法来解决非线性ODE,该方法基于来自" Python中工程的数值"作者:Jaan Kiusalaas。 但是我得到了#34;矩阵是单数的"当我运行程序时。有人可以解释一下吗?提前谢谢。
*编辑:这是我的代码
#Nonlinear ODE
# (1+K)f''' + ff''-f'^2-Kh'=0
# (1+0.5K)h''+fh'-f'h-K(2h+f'')=0
# theta'' + P f theta'=0
#subject to boundary condition
# f(0)=0, f'(0)= 1, h(0)=-nf''(0), theta(0)=1
#!/usr/bin/python
import matplotlib.pyplot as plt
import numpy as np
from run_kut5 import *
from newtonRaphson2 import *
from printSoln import *
K = [0.0,1.0,2.0]
n = 0.5
lam = 0
P = 1
u_1 = 0.0 #initial values for y1
u_2 = 1.0 #initial values for y2
u_3 = lam #initial values for y3
u_7 = 1
i = 0 #index
while i < len(K):
K1 = K[i]
def initCond(u): # Initial values of [y,y',y",y"'];
# use 'u' if unknown
return np.array([u_1, u_2, lam, u[0],-n*u[0],u[1],u_7,u[2]])
def r(u): # Boundary condition residuals-- see Eq. (8.7)
r = np.zeros(len(u))
X,Y = integrate(F,x,initCond(u),xStop,h)
y = Y[len(Y) - 1]
r[0] = y[4]
r[1] = y[6]
r[2] = y[7]
return r
def F(x,y): # First-order differential equations
F = np.zeros(8)
F[0] = 1
F[1] = y[3]
F[2] = y[4]
F[3] = (y[3]-K1*y[6]-y[2]*y[4])/(1+K1)
F[4] = y[6]
F[5] = (y[5]*y[3]-y[2]*y[6]+K1*(2*y[5]+y[4]))/(1+K1*0.5)
F[6] = y[7]
F[7] = -P*y[2]*y[7]
return F
x = 0.0 # Start of integration
xStop = 11.0 # End of integration
u = np.array([-1,-0.5,-5]) # Initial guess for u
h = 0.001 # Initial step size
freq = 0 # Printout frequency
u = newtonRaphson2(r,u,1.0e-4)
X,Y = integrate(F,x,initCond(u),xStop,h)
printSoln(X,Y,freq)
i += 1
plt.plot(X, Y[:,1], label='y\'',)
plt.legend()
plt.show()
input("\nPress return to exit")
## module newtonRaphson2
''' soln = newtonRaphson2(f,x,tol=1.0e-9).
Solves the simultaneous equations f(x) = 0 by
the Newton-Raphson method using {x} as the initial
guess. Note that {f} and {x} are vectors.
'''
import numpy as np
from gaussPivot import *
import math
def newtonRaphson2(f,x,tol=1.0e-9):
def jacobian(f,x):
h = 1.0e-4
n = len(x)
jac = np.zeros((n,n))
f0 = f(x)
for i in range(n):
temp = x[i]
x[i] = temp + h
f1 = f(x)
x[i] = temp
jac[:,i] = (f1 - f0)/h
return jac,f0
for i in range(30):
jac,f0 = jacobian(f,x)
if math.sqrt(np.dot(f0,f0)/len(x)) < tol:
return x
dx = gaussPivot(jac,-f0)
x = x + dx
if math.sqrt(np.dot(dx,dx)) < tol*max(max(abs(x)),1.0): return x
print('Too many iterations')
## module run_kut5
''' X,Y = integrate(F,x,y,xStop,h,tol=1.0e-6)
Adaptive Runge-Kutta method with Dormand-Prince
coefficients for solving the
initial value problem {y}' = {F(x,{y})}, where
{y} = {y[0],y[1],...y[n-1]}.
x,y = initial conditions
xStop = terminal value of x
h = initial increment of x used in integration
tol = per-step error tolerance
F = user-supplied function that returns the
array F(x,y) = {y'[0],y'[1],...,y'[n-1]}
'''
import math
import numpy as np
def integrate(F,x,y,xStop,h,tol=1.0e-6):
a1 = 0.2; a2 = 0.3; a3 = 0.8; a4 = 8/9; a5 = 1.0
a6 = 1.0
c0 = 35/384; c2 = 500/1113; c3 = 125/192
c4 = -2187/6784; c5 = 11/84
d0 = 5179/57600; d2 = 7571/16695; d3 = 393/640
d4 = -92097/339200; d5 = 187/2100; d6 = 1/40
b10 = 0.2
b20 = 0.075; b21 = 0.225
b30 = 44/45; b31 = -56/15; b32 = 32/9
b40 = 19372/6561; b41 = -25360/2187; b42 = 64448/6561
b43 = -212/729
b50 = 9017/3168; b51 =-355/33; b52 = 46732/5247
b53 = 49/176; b54 = -5103/18656
b60 = 35/384; b62 = 500/1113; b63 = 125/192
b64 = -2187/6784; b65 = 11/84
X = []
Y = []
X.append(x)
Y.append(y)
stopper = 0 #Integration stopper(0 = off, 1 = on)
k0 = h*F(x,y)
for i in range(500):
k1 = h*F(x + a1*h, y + b10*k0)
k2 = h*F(x + a2*h, y + b20*k0 + b21*k1)
k3 = h*F(x + a3*h, y + b30*k0 + b31*k1 + b32*k2)
k4 = h*F(x + a4*h, y + b40*k0 + b41*k1 + b42*k2 + b43*k3)
k5 = h*F(x + a5*h, y + b50*k0 + b51*k1 + b52*k2 + b53*k3 + b54*k4)
k6 = h*F(x + a6*h, y + b60*k0 + b62*k2 + b63*k3 + b64*k4 + b65*k5)
dy = c0*k0 + c2*k2 + c3*k3 + c4*k4 + c5*k5
E = (c0 - d0)*k0 + (c2 - d2)*k2 + (c3 - d3)*k3 + (c4 - d4)*k4 + (c5 - d5)*k5 - d6*k6
e = math.sqrt(np.sum(E**2)/len(y))
hNext = 0.9*h*(tol/e)**0.2
# Accept integration step if error e is within tolerance
if e <= tol:
y = y + dy
x = x+ h
X.append(x)
Y.append(y)
if stopper == 1: break #Reached end of x-range
if abs(hNext) > 10.0*abs(h): hNext = 10.0*h
# Check if next step is the last one; if so, adjust h
if (h > 0.0) == ((x + hNext) >= xStop):
hNext = xStop - x
stopper = 1
k0 = k6*hNext/h
else:
if abs(hNext) < 0.1*abs(h): hNext = 0.1*h
k0 = k0*hNext/h
h = hNext
return np.array(X), np.array(Y)
## module printSoln
'''printSoln(X,Y,freq).
Prints X and Y returned from the differential equation solvers using
printout frequency ’freq’.
freq = n prints every nth step.
freq = 0 prints initial and final values only.
'''
def printSoln(X,Y,freq):
def printHead(n):
print("\n x ", end=" ")
for i in range(n):
print(" y[",i,"]", end=" ")
print()
def printLine(x,y,n):
print("{:13.4e}".format(x),end=" ")
for i in range(n):
print(":13.4e".format(y[i]),end=" ")
print()
m = len(Y)
try: n = len(Y[0])
except TypeError: n = 1
if freq == 0: freq = m
printHead(n)
for i in range(0,m,freq):
printLine(X[i],Y[i],n)
if i != m - 1: printLine(X[m - 1],Y[m - 1],n)
##module gaussPivot
''' x = gaussPivot(a,b,tol=1.0e-22).
Solves [a]{x} = {b} by Gauss elimination with
scaled row pivoting
'''
import numpy as np
import swap
import error
def gaussPivot(a,b,tol=1.0e-12):
n = len(b)
#Set up scale factors
s = np.zeros(n)
for i in range(n):
s[i] = max(np.abs(a[i,:]))
for k in range(0,n-1):
#Row interchange, if needed
p = np.argmax(np.abs(a[k:n,k])/s[k:n]) + k
if abs(a[p,k]) < tol: error.err('Matrix is singular')
if p != k:
swap.swapRows(b,k,p)
swap.swapRows(s,k,p)
swap.swapRows(a,k,p)
#Elimination
for i in range(k+1,n
):
if a[i,k] != 0.0:
lam = a[i,k]/a[k,k]
a[i,k+1:n] = a[i,k+1:n] - lam*a[k,k+1:n]
b[i] = b[i] - lam*b[k]
if abs(a[n-1,n-1]) < tol: error.err('Matrix is singular')
#Back substitution
b[n-1] = b[n-1]/a[n-1,n-1]
for k in range(n-2.-1,-1):
b[k] = (b[k] - np.dot(a[k,k+1:n],b[k+1:n]))/a[k,k]
return b
答案 0 :(得分:0)
你的ODE函数没有实现给定的方程,似乎是自治与非自治公式和python与matlab索引的混合。由于您的州有8个组件,订单总和3 + 2 + 2 = 7,第一个组件必须是额外的x
,因此状态向量是
y = [ x, f, f', f'', h, h', theta, theta' ]
这应该反映在衍生物和初始值
中#Nonlinear ODE
# (1+K)f''' + ff''-f'^2-Kh'=0
# (1+0.5K)h''+fh'-f'h-K(2h+f'')=0
# theta'' + P f theta'=0
#subject to boundary condition
# f(0)=0, f'(0)= 1, h(0)=-nf''(0), theta(0)=1
def F(x,y): # First-order differential equations
F = np.zeros(8)
# f0, f1, f2,
F[0] = 1 # dx/dx=1
F[1] = y[2] # df/dx=f'
F[2] = y[3] # df'/dx=f''
F[3] = (y[2]**2+K1*y[5]-y[1]*y[2])/(1+K1)
F[4] = y[5] # dh/dx = h'
F[5] = (y[4]*y[2]-y[1]*y[5]+K1*(2*y[4]+y[3]))/(1+K1*0.5)
F[6] = y[7] dtheta/dx = theta'
F[7] = -P*y[1]*y[7]
return F
def initCond(u): # Initial values of [ x, f, f', f'', h, h', th, th' ];
# use 'u' if unknown
return np.array([ 0.0, u_1, u_2, u[0], -n*u[0], u[1], u_7, u[2]])
我无法看到lam
与给定系统的关系。由于没有记录第二个边界xStop
的边界条件,您必须自己检查并纠正索引问题。
以前的答案一般都是正确的:
任何问题F(x)=0
的牛顿方法都需要系统的解决方案
F'(x)*s = -F(x).
如果存在多种解决方案,因为对于非线性ODE的BVP的拍摄问题可能会发生,那么在解决方案之间存在矩阵F'(x)
是单数或近似的区域。
您需要更好地猜测方法的初始值,或者您需要切换到多次拍摄方法,在这种情况下,使用合理的初始路径,您不太可能陷入单一情境。
错误消息是接近零的绝对测试的结果,不涉及规模。将问题重新缩放到接近1的范围可能会有所帮助,0.01到1000的范围应该很好。
或者,切换到scipy.integrate
和scipy.linalg
中更专业的工具,这些工具应该更好地处理这些近似单一的案例。