scipy.integrate.solve_bvp

scipy.integrate.solve_bvp(fun, bc, x, y, p=None, S=None, fun_jac=None, bc_jac=None, tol=0.001, max_nodes=1000, verbose=0, bc_tol=None)[源代码]

解一个常微分方程组的边值问题。

此函数以数值方式求解受两点边界条件约束的一阶常微分方程系统:

dy / dx = f(x, y, p) + S * y / (x - a), a <= x <= b
bc(y(a), y(b), p) = 0

这里x是一维自变量,y(X)是N维矢量值函数,p是未知参数的k-D矢量,它将与y(X)一起找到。对于要确定的问题,必须有n+k个边界条件,即BC必须是(n+k)-D函数。

系统右侧的最后一个单数项是可选的。它由n乘以n的矩阵S定义,使得解必须满足Sy(A)=0。此条件将在迭代过程中强制执行,因此它不能与边界条件冲突。看见 [2] 用于解释在数值求解边值问题时如何处理这一项。

复杂领域中的问题也可以解决。在这种情况下,y和p被认为是复数,f和bc被假定为复值函数,但x保持为实数。请注意,f和bc必须是复数可微的(满足Cauchy-Riemann方程 [4]) ,否则你应该分别重写实部和虚部的问题。要解决复杂领域中的问题,请使用复杂数据类型传递y的初始猜测(见下文)。

参数
fun可调用

系统的右侧。调用签名为 fun(x, y) ,或 fun(x, y, p) 如果参数存在。所有参数都是ndarray: x 形状为(m,), y 形状为(n,m),意思是 y[:, i] 对应于 x[i] ,以及 p 形状为(k,)。返回值必须是形状为(n,m)且布局与相同的数组 y

bc可调用

计算边界条件残差的函数。调用签名为 bc(ya, yb) ,或 bc(ya, yb, p) 如果参数存在。所有参数都是ndarray: yayb 形状(n,),和 p 形状为(k,)。返回值必须是形状为(n+k,)的数组。

x类似阵列,形状(m,)

初始网格。必须是一个严格递增的实数序列 x[0]=ax[-1]=b

y类似数组,形状(n,m)

对网格节点处函数值的初始猜测,第i列对应于 x[i] 。对于复杂域过程中的问题 y 具有复杂数据类型(即使最初的猜测是纯真实的)。

p具有形状(k,)的ARRAY_LIKE或NONE,可选

对未知参数的初步猜测。如果为None(默认值),则假定问题与任何参数无关。

S具有Shape(n,n)或None的ARRAY_LIKE

定义单数项的矩阵。如果为None(默认值),则不使用单数项即可解决问题。

fun_jac可调用或无,可选

函数计算f关于y和p的导数。调用签名是 fun_jac(x, y) ,或 fun_jac(x, y, p) 如果参数存在。返回必须按以下顺序包含1或2个元素:

  • df_dy:形状为(n,n,m)的类似数组,其中元素(i,j,q)等于df_i(x_q,y_q,p)/d(Y_Q)_j。

  • df_dp:形状为(n,k,m)的类似数组,其中元素(i,j,q)等于df_i(x_q,y_q,p)/dp_j。

这里,q表示定义了x和y的节点,而i和j表示矢量分量。如果在没有未知参数的情况下解决问题,则不应返回df_dp。

如果 fun_jac 为NONE(默认值),则导数将由正向有限差分估计。

bc_jac可调用或无,可选

函数计算BC关于ya、yb和p的导数。调用签名为 bc_jac(ya, yb) ,或 bc_jac(ya, yb, p) 如果参数存在。返回必须包含2或3个元素,顺序如下:

  • dbc_dya:形状为(n,n)的类似数组,其中元素(i,j)等于dbc_i(ya,yb,p)/dya_j。

  • dbc_dyb:形状为(n,n)的类似数组,其中元素(i,j)等于dbc_i(ya,yb,p)/dyb_j。

  • dbc_dp:形状为(n,k)的类似数组,其中元素(i,j)等于dbc_i(ya,yb,p)/dp_j。

如果在没有未知参数的情况下解决问题,则不应返回DBC_DP。

如果 bc_jac 为NONE(默认值),则导数将由正向有限差分估计。

tol浮动,可选

解决方案的所需公差。如果我们定义 r = y' - f(x, y) ,其中y是找到的解,则求解器尝试在每个网格间隔上实现 norm(r / (1 + abs(f)) < tol ,在哪里 norm 以均方根意义估计(使用数值求积公式)。默认值为1e-3。

max_nodes整型,可选

允许的最大网格节点数。如果超过,算法将终止。默认值为1000。

verbose{0,1,2},可选

算法的详细程度:

  • 0(默认值):静默工作。

  • 1:显示终止报告。

  • 2:显示迭代过程中的进度。

bc_tol浮动,可选

边界条件残差的所需绝对公差: bc 值应满足 abs(bc) < bc_tol 组件方式。等于 tol 默认情况下。最多允许10次迭代才能达到此容差。

退货
捆绑定义了以下字段的对象:
solPPoly

找到Y AS的解决方案 scipy.interpolate.PPoly 实例,C1连续三次样条曲线。

pndarray或None,Shape(k,)

找到参数。如果问题中不存在参数,则返回None。

xndarray,形状(m,)

最终网格的节点。

yndarray,形状(n,m)

网格节点处的解算值。

ypndarray,形状(n,m)

网格节点处的解导数。

rms_residualsndarray,形状(m-1,)

每个网格间隔内的相对残差值的均方根值(请参阅的说明 tol 参数)。

niter集成

已完成的迭代数。

status集成

算法终止原因:

  • 0:算法收敛到所需的精度。

  • 1:已超过最大网格节点数。

  • 2:求解配置系统时遇到的奇异雅可比。

message字符串

终止原因的口头描述。

success布尔尔

如果算法收敛到所需的精度,则为true (status=0 )。

注意事项

此函数实现四阶配置算法,其残差控制类似于 [1]. 配置系统由带有仿射不变准则函数的阻尼牛顿法求解,如中所述 [3].

请注意,在 [1] 积分残差是在没有按区间长度进行归一化的情况下定义的。因此,它们的定义与这里使用的定义不同,乘数为h**0.5(h是区间长度)。

0.18.0 新版功能.

参考文献

1(1,2)

J.Kierzenka,L.F.Shampine,“一个基于残差控制和Maltab PSE的BVP求解器”,ACM Trans。数学课。软文,第27卷,第3号,第299-316页,2001年。

2

作者:L.F.Shampine,P.H.Muir,H.Xu,“一个界面友好的Fortran BVP求解器”。

3

U.Ascher,R.Mattheij和R.Russell,“常微分方程边值问题的数值解”。

4

Cauchy-Riemann equations 在维基百科上。

示例

在第一个示例中,我们解决了Bratu的问题:

y'' + k * exp(y) = 0
y(0) = y(1) = 0

对于k=1。

我们将方程重写为一阶系统,并实现其右侧求值:

y1' = y2
y2' = -exp(y1)
>>> def fun(x, y):
...     return np.vstack((y[1], -np.exp(y[0])))

实施边界条件残差的评估:

>>> def bc(ya, yb):
...     return np.array([ya[0], yb[0]])

定义具有5个节点的初始网格:

>>> x = np.linspace(0, 1, 5)

众所周知,这个问题有两种解决方案。为了同时获得它们,我们对y使用两种不同的初始猜测。我们用下标a和b表示它们。

>>> y_a = np.zeros((2, x.size))
>>> y_b = np.zeros((2, x.size))
>>> y_b[0] = 3

现在我们准备好运行求解器了。

>>> from scipy.integrate import solve_bvp
>>> res_a = solve_bvp(fun, bc, x, y_a)
>>> res_b = solve_bvp(fun, bc, x, y_b)

让我们画出这两个找到的解决方案。我们利用样条形式的解来生成平滑的图形。

>>> x_plot = np.linspace(0, 1, 100)
>>> y_plot_a = res_a.sol(x_plot)[0]
>>> y_plot_b = res_b.sol(x_plot)[0]
>>> import matplotlib.pyplot as plt
>>> plt.plot(x_plot, y_plot_a, label='y_a')
>>> plt.plot(x_plot, y_plot_b, label='y_b')
>>> plt.legend()
>>> plt.xlabel("x")
>>> plt.ylabel("y")
>>> plt.show()
../../_images/scipy-integrate-solve_bvp-1_00_00.png

我们看到这两个解决方案具有相似的形状,但在规模上有很大的不同。

在第二个示例中,我们解决了一个简单的Sturm-Liouville问题:

y'' + k**2 * y = 0
y(0) = y(1) = 0

已知非平凡解y=A SIN(k x)对于k=π*n是可能的,其中n是整数。为了建立归一化常数A=1,我们添加一个边界条件:

y'(0) = k

同样,我们将方程重写为一阶系统,并实现其右侧求值:

y1' = y2
y2' = -k**2 * y1
>>> def fun(x, y, p):
...     k = p[0]
...     return np.vstack((y[1], -k**2 * y[0]))

请注意,参数p作为向量传递(在我们的示例中只有一个元素)。

实施边界条件:

>>> def bc(ya, yb, p):
...     k = p[0]
...     return np.array([ya[0], yb[0], ya[1] - k])

设置初始网格并猜测y。我们的目标是找到k=2的解 Pi,为了实现这一点,我们将y的值设置为大致跟随sin(2 Pi*x):

>>> x = np.linspace(0, 1, 5)
>>> y = np.zeros((2, x.size))
>>> y[0, 1] = 1
>>> y[0, 3] = -1

使用6作为k的初始猜测来运行求解器。

>>> sol = solve_bvp(fun, bc, x, y, p=[6])

我们看到找到的k大致正确:

>>> sol.p[0]
6.28329460046

最后,绘制解决方案以查看预期的正弦曲线:

>>> x_plot = np.linspace(0, 1, 100)
>>> y_plot = sol.sol(x_plot)[0]
>>> plt.plot(x_plot, y_plot)
>>> plt.xlabel("x")
>>> plt.ylabel("y")
>>> plt.show()
../../_images/scipy-integrate-solve_bvp-1_01_00.png