【问题标题】:Numerical ODE solving in PythonPython 中的数值 ODE 求解
【发布时间】:2017-09-07 09:35:01
【问题描述】:

如何在 Python 中对 ODE 进行数值求解?

考虑

\ddot{u}(\phi) = -u + \sqrt{u}

满足以下条件

u(0) = 1.49907

\dot{u}(0) = 0

有约束

0 <= \phi <= 7\pi.

最后,我想生成一个参数图,其中 x 和 y 坐标是作为 u 的函数生成的。

问题是,我需要运行 odeint 两次,因为这是一个二阶微分方程。 我尝试在第一次之后再次运行它,但它返回雅可比错误。一定有办法一次运行两次。

这是错误:

odepack.error: 函数及其 Jacobian 必须是可调用函数

下面的代码生成。有问题的行是 sol = odeint。

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


def f(u, t):
    return -u + np.sqrt(u)


times = linspace(0.0001, 7 * np.pi, 1000)
y0 = 1.49907
yprime0 = 0
yvals = odeint(f, yprime0, times)

sol = odeint(yvals, y0, times)

x = 1 / sol * np.cos(times)
y = 1 / sol * np.sin(times)

plot(x,y)

plt.show()

编辑

我正在尝试构建第 9 页的情节

Classical Mechanics Taylor

这是 Mathematica 的情节

In[27]:= sol = 
 NDSolve[{y''[t] == -y[t] + Sqrt[y[t]], y[0] == 1/.66707928, 
   y'[0] == 0}, y, {t, 0, 10*\[Pi]}];

In[28]:= ysol = y[t] /. sol[[1]];

In[30]:= ParametricPlot[{1/ysol*Cos[t], 1/ysol*Sin[t]}, {t, 0, 
  7 \[Pi]}, PlotRange -> {{-2, 2}, {-2.5, 2.5}}]

【问题讨论】:

标签: python plot numerical-methods differential-equations


【解决方案1】:
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import numpy as np

pi = np.pi
sqrt = np.sqrt
cos = np.cos
sin = np.sin

def deriv_z(z, phi):
    u, udot = z
    return [udot, -u + sqrt(u)]

phi = np.linspace(0, 7.0*pi, 2000)
zinit = [1.49907, 0]
z = integrate.odeint(deriv_z, zinit, phi)
u, udot = z.T
# plt.plot(phi, u)
fig, ax = plt.subplots()
ax.plot(1/u*cos(phi), 1/u*sin(phi))
ax.set_aspect('equal')
plt.grid(True)
plt.show()

【讨论】:

  • 应该是zinit = [1.49907, 0](错位点)。
  • @jorgeca:谢谢。我没有意识到问题已经改变了。
【解决方案2】:

来自您其他question 的代码非常接近您想要的。需要进行两项更改:

  • 您正在求解不同的 ODE(因为您更改了函数 deriv 内的两个符号)
  • 所需绘图的y 分量来自解值,而不是解的一阶导数的值,因此您需要将u[:,0](函数值)替换为u[:, 1](导数)。

这是最终结果:

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

def deriv(u, t):
    return np.array([u[1], -u[0] + np.sqrt(u[0])])

time = np.arange(0.01, 7 * np.pi, 0.0001)
uinit = np.array([1.49907, 0])
u = odeint(deriv, uinit, time)

x = 1 / u[:, 0] * np.cos(time)
y = 1 / u[:, 0] * np.sin(time)

plt.plot(x, y)
plt.show()

但是,我建议您使用 unutbu 答案中的代码,因为它是自我记录的 (u, udot = z) 并使用 np.linspace 而不是 np.arange。然后,运行它以获得您想要的数字:

x = 1 / u * np.cos(phi)
y = 1 / u * np.sin(phi)
plt.plot(x, y)
plt.show()

【讨论】:

    【解决方案3】:

    您可以使用 scipy.integrate.ode。要解决 dy/dt = f(t,y),初始条件 y(t0)=y0,在 time=t1 和 4 阶 Runge-Kutta 时,您可以执行以下操作:

    from scipy.integrate import ode
    solver = ode(f).set_integrator('dopri5')
    solver.set_initial_value(y0, t0)
    dt = 0.1
    while t < t1:
        y = solver.integrate(t+dt)
        t += dt
    

    编辑:您必须得到一阶导数才能使用数值积分。这可以通过设置来实现,例如z1=u 和 z2=du/dt,之后有 dz1/dt = z2 和 dz2/dt = d^2u/dt^2。将这些代入您的原始方程,然后简单地迭代向量 dZ/dt,这是一阶的。

    编辑 2:这是整个事情的示例代码:

    import numpy as np
    import matplotlib.pyplot as plt
    
    from numpy import sqrt, pi, sin, cos
    from scipy.integrate import ode
    
    # use z = [z1, z2] = [u, u']
    # and then f = z' = [u', u''] = [z2, -z1+sqrt(z1)]
    def f(phi, z):
        return [z[1], -z[0]+sqrt(z[0])]
    
    
    # initialize the 4th order Runge-Kutta solver
    solver = ode(f).set_integrator('dopri5')
    
    # initial value
    z0 = [1.49907, 0.]
    solver.set_initial_value(z0)
    
    values = 1000
    phi = np.linspace(0.0001, 7.*pi, values)
    u = np.zeros(values)
    
    for ii in range(values):
        u[ii] = solver.integrate(phi[ii])[0] #z[0]=u
    
    x = 1. / u * cos(phi)
    y = 1. / u * sin(phi)
    
    plt.figure()
    plt.plot(x,y)
    plt.grid()
    plt.show()
    

    【讨论】:

    • 当然,我已将其添加为编辑。我更喜欢使用更灵活的 scipy.integrate.ode 而不是 odeint,尽管设置起来可能会有点复杂。
    【解决方案4】:

    scipy.integrate() 进行 ODE 集成。这就是你要找的吗?

    【讨论】:

      猜你喜欢
      • 2015-12-01
      • 2016-01-24
      • 2019-07-05
      • 1970-01-01
      • 2019-06-07
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      • 2019-03-02
      相关资源
      最近更新 更多