如何整合所有间隔?

时间:2018-05-01 18:13:16

标签: python numpy scipy ode differential-equations

我正在尝试在python的numpy中实现euler-method,runge-kutta和middle-point-method。

我想实现一个函数integrateall,它取决于所有间隔的方法(euler,runge-kutta或中间点)。其他函数(euler,rk4和middlepoint)必须只执行一步并返回值。

这是我的python代码:

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

def oneStepProcess(y, t, h, ode):
    ''' 
    just to represents the form that one-step-processes that follow (euler, rk4 and middlepoint) should have 
    :param y: last calculated value at time t; y_k hast n components 
      y is an np.array
    :param t: time t of last calculated step 
    :param h: timestep
    :param ode: ode to solve as function
    :return: y_new Solution at time t_{new}=t+h (as np.array)
    '''

def euler(y, t, h, ode):
    y_new = y + ode(t, y)*h
    return y_new

def f1(t, y):
    return -5*y


def fn(t, y):
    dy = np.zeros_like(y)
    dy[0] = -5*y[1]
    dy[1] = 3*y[0]+y[1]
    return dy


assert np.allclose(euler(np.array([1.]), 1., 0.1, f1), np.array([0.5]))
assert np.allclose(euler(np.array([1., -1]), 1., 0.2, fn), np.array([2, -0.6]))


def middlepoint(y, t, h, ode):
    mid_step = 0.5*h
    y_mid = y + (mid_step*ode(t, y))
    ynew = y + h*ode(y_mid, t+mid_step)
    return ynew

def rk4(y, t, h, ode):
    mid_step = 0.5*h
    k1 = y
    k2 = ode(t+mid_step, y+(mid_step*k1))
    k3 = ode(t+mid_step, y+(mid_step*k2))
    k4 = ode(t+h, y+(h*k3))
    ynew = y + (h*((k1/6)+(k2/3)+(k3/3)+(k4/6)))
    return ynew

def integrateall(method, ode, y0, t0, tend, N, intermediate=False):
    """

    :param method: one-step-process
    :param ode: RHS of ode 
    :param y0: Start value 
    :param t0: Start time
    :param tend: End time
    :param N: Number of steps
    :param intermediate: True, if we want to show intermediate solutions when plotting 
    :return: Solution at endtime (or solution AND intermediate times, if intermediate=True)
    """

    tvals = [t0]
    yvals = [y0]
    t = t0
    y = y0
    h = (tend-t0/N)
    while t < tend:
        h = min(h, tend-t)
        #t ,y = method(yvals[-1],tvals[-1],h,ode)
        t ,y = method(y,t,h,ode)
        tvals.append(t)
        yvals.append(y)

    yend = yvals[-1]
    print(yvals)

    if intermediate:
        return np.array(yvals), np.array(tvals)
    else:
        return yend


def mouse(t, y):
    dy = np.array([1 - (y[0]+y[1]), y[0]-y[1]])
    return dy

def fsimple(t, y):
    return t, y

y= integrateall(euler, fsimple, np.array([1.,]), 0., 1., 100, False) 

# wanted to test the numpy-functions 'euler', 'rk4' and 'middlepoint' in 'integrateall' with the functions fsimple, mouse, f1 and fn but none works. Just left the first one (fsimple) for clarity. If you could help me why euler, rk4 and middlepoint are not working with 'integrateall' as well, it would be perfect! 

以下是我得到的错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-17-6624311b1e40> in <module>()
     90     return t, y
     91 
---> 92 y= integrateall(euler, fsimple, np.array([1.,]), 0., 1., 100, False)
     93 

<ipython-input-17-6624311b1e40> in integrateall(method, ode, y0, t0, tend, N, intermediate)
     71         h = min(h, tend-t)
     72         #t ,y = method(yvals[-1],tvals[-1],h,ode)
---> 73         t ,y = method(y,t,h,ode)
     74         tvals.append(t)
     75         yvals.append(y)

<ipython-input-17-6624311b1e40> in euler(y, t, h, ode)
     17 
     18 def euler(y, t, h, ode):
---> 19     y_new = y + ode(t, y)*h
     20     return y_new
     21 

TypeError: can't multiply sequence by non-int of type 'float'

1 个答案:

答案 0 :(得分:0)

“fsimple”应该返回一个数组

def fsimple(t, y):
    return np.array([t, y])

因为(t,y)无法按方法h中的步骤euler进行元素倍增。

编辑:参数顺序和返回值的约定在代码中不一致。请参阅评论中的更改

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

def oneStepProcess(y, t, h, ode):
    ''' 
    just to represents the form that one-step-processes that follow (euler, rk4 and middlepoint) should have 
    :param y: last calculated value at time t; y_k hast n components 
      y is an np.array
    :param t: time t of last calculated step 
    :param h: timestep
    :param ode: ode to solve as function
    :return: y_new Solution at time t_{new}=t+h (as np.array)
    '''

def euler(y, t, h, ode):
    y_new = y + ode(y, t)*h # Fix argument order
    return y_new

def f1(y, t): # Fix argument order
    return -5*y


def fn(y, t): # Fix argument order
    dy = np.zeros_like(y)
    dy[0] = -5*y[1]
    dy[1] = 3*y[0]+y[1]
    return dy


assert np.allclose(euler(np.array([1.]), 1., 0.1, f1), np.array([0.5]))
assert np.allclose(euler(np.array([1., -1]), 1., 0.2, fn), np.array([2, -0.6]))


def middlepoint(y, t, h, ode):
    mid_step = 0.5*h
    y_mid = y + (mid_step*ode(y, t)) # Fix argument order
    ynew = y + h*ode(y_mid, t+mid_step)
    return ynew

def rk4(y, t, h, ode):
    mid_step = 0.5*h
    k1 = y
    k2 = ode(y+(mid_step*k1), t+mid_step) # Fix argument order
    k3 = ode(y+(mid_step*k2), t+mid_step) # Fix argument order
    k4 = ode(y+(h*k3), t+mid_step) # Fix argument order
    ynew = y + (h*((k1/6)+(k2/3)+(k3/3)+(k4/6)))
    return ynew

def integrateall(method, ode, y0, t0, tend, N, intermediate=False):
    """

    :param method: one-step-process
    :param ode: RHS of ode 
    :param y0: Start value 
    :param t0: Start time
    :param tend: End time
    :param N: Number of steps
    :param intermediate: True, if we want to show intermediate solutions when plotting 
    :return: Solution at endtime (or solution AND intermediate times, if intermediate=True)
    """

    tvals = [t0]
    yvals = [y0]
    t = t0
    y = y0
    h = (tend-t0)/N # Here, the whole time interval must be divided by N
    while t < tend:
        y = method(y,t,h,ode) # Fix argument order *and* remove t from the return value
        t += h
        tvals.append(t)
        yvals.append(y)

    yend = yvals[-1]
    print(yvals)

    if intermediate:
        return np.array(yvals), np.array(tvals)
    else:
        return yend


def mouse(y, t): # Fix argument order
    dy = np.array([1 - (y[0]+y[1]), y[0]-y[1]])
    return dy

def fsimple(y, t): # Fix argument order
    return y # Remove t from the return value