拍摄方法奇异矩阵警告

时间:2019-03-04 19:23:14

标签: python scipy differential-equations numerical-integration

我正在尝试在python中实现射击方法,并且遇到有关奇异矩阵的错误消息。我想用边界条件xy''' = -y'y+x^2-1解决y(1)=1, y(2)=1, y'(2)=4。我将其转换为一阶方程M(x, y y '= f(x, y ),其中:

M = [1  0  0]
    [0  1  0]
    [0  0  x]

**y** = [ y ]
        [ y']
        [y''] 
f = [     y'   ]
    [     y''  ]
    [-y'y+x^2-1]

我已经按照数字食谱18.1中所述实施了射击方法。常规步骤是:

  1. 在左右两侧都指定边界条件。在我的情况下,这是y(1)= 1,y(2)= 1,y'(2)= 4。提供(y,y',y'')向量作为初始猜测。我选择了(1、2、1)。第一个元素固定为y(1)= 1,但是其他两个元素组成了可自由指定的向量 V ,其中包含LHS上的其他边界条件。
  2. 对等式x = 1到x = 2进行积分。定义差异向量 F ,该差异向量是通过规定的边界条件(y(2)= 1和y'(2)= 4)与通过积分计算的值之间的差计算得出的。
  3. 使用Newton-Raphson查找将 F 置零的 V 。通过求解 J V = -F 并将校正添加回 V 即可完成此操作。 J 是雅可比行列式,在每个时间步均通过数字找到。

如果我假设M是可逆的,那么这相对容易解决并且可以按预期工作。但是,我遇到了两个问题,希望能为您解决这些问题提供一些帮助。

  1. 如果将xrange从-1设置为1,则会收到奇异矩阵警告。我认为这是因为我假设M是可逆的,但是当x = 0时不是。如果M对 y 有任何依赖性,则经常会出现相同的问题。这看起来像是微分代数方程。有什么方便的方法可以在python中解决此问题吗?看起来scipy中没有内置任何内容。
  2. 如果我将x = 2处的边界条件更改为y(2)=-1,也会收到奇异矩阵警告。我不确定为什么会这样。这是否可能只是微分方程无法满足边界条件或我的代码有问题?如果是前者,有什么办法可以提前预测到这一点?

A previous answer建议,在第二种奇异情况下,更好地猜测初始条件可能会有所帮助,但是我不确定如何在没有大量蛮力的情况下改善猜测。

这是我的代码:

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint

### Solving the differential equation xy''' = -y'y+x^2-1  
### if x range doesn't include 0, then M is invertible, so equations become:
def fn(y,x):
    return np.array([y[1],y[2],(-y[0]*y[1]+x**2-1)/x])

### Boundary values of y(1)=1, y(2)=1, y'(2)=4

x = np.linspace(1.,2.,101)
y0 = np.array([1., 1., 2.]) ### Only the first of these is fixed. The others are variable
sol = np.array([1.,4.]) ### Specify y(2)=1 y'(2)=4
Delta=1
max_itrs = 100

eps = 1e-3

def shoot(y0, Delta, fn, x, sol, eps, max_itrs):
    ### Throughtout this function, the indexing only works with our specific boundary conditions
    ### But can be easily modified or generalized to accommodate other cases
    for i in range(max_itrs):
        F = odeint(fn, y0, x)[-1][:2]-sol ### Define the F "discrepancy" vector at x = 2
        if np.linalg.norm(F)>eps:
            y0_yp_mod = y0.copy()
            y0_yp_mod[1] += Delta ### Augment y'(1) by Delta
            F_yp_mod = odeint(fn, y0_yp_mod, x)[-1][:2]-sol 

            y0_ypp_mod = y0.copy()
            y0_ypp_mod[2] += Delta ### Augment y''(1) by Delta
            F_ypp_mod = odeint(fn, y0_ypp_mod, x)[-1][:2]-sol

            ### Compute the Jacobian
            j11 = (F_yp_mod[0]-F[0])/Delta
            j12 = (F_yp_mod[1]-F[1])/Delta
            j21 = (F_ypp_mod[0]-F[0])/Delta
            j22 = (F_ypp_mod[1]-F[1])/Delta
            jac = np.array([[j11,j12],[j21,j22]])

            ### Solve for delta V:
            d_V = np.linalg.solve(jac,-F)

            ### Augment our initial conditions by delta V
            y0 = np.array([y0[0],y0[1]+d_V[0],y0[2]+d_V[1]])
        else:
            return y0
    print("Failed to achieve convergence after "+str(max_itrs)+" iterations.")
    return y0

y0 = shoot(y0, Delta, fn, x, sol, eps, max_itrs)
Y = odeint(fn, y0, x)
plt.plot(x,Y[:,0],c="r",label="y")
plt.plot(x,Y[:,1],c="g",label="y'")
plt.plot(x,Y[:,2],c="b",label="y''")
plt.scatter([x[0]],y0[0],marker="x",c="r")
plt.scatter([x[-1]],sol[0],marker="x",c="r")
plt.scatter([x[-1]],sol[1],marker="x",c="g")
plt.legend()

Here是输出,其行为符合预期 output

0 个答案:

没有答案