我正在尝试用 Python 求解耦合一阶 ODE 系统。我对此很陌生,但是僵尸启示录示例 http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT到目前为止,SciPy.org 提供了很大的帮助。
在我的例子中,一个重要的区别是用于“驱动”我的 ODE 系统的输入数据发生了变化abruptly在不同的时间点,我不知道如何最好地处理这个问题。下面的代码是我能想到的用来说明我的问题的最简单的示例。我很欣赏这个例子有一个简单的解析解,但我的实际 ODE 系统更复杂,这就是为什么我试图理解数值方法的基础知识。
简化示例
Consider a bucket with a hole in the bottom (this kind of "linear reservoir" is the basic building block of many hydrological models). The input flow rate to the bucket is R and the output from the hole is Q. Q is assumed to be proportional to the volume of water in the bucket, V. The constant of proportionality is usually written as
, where T is the "residence time" of the store. This gives a simple ODE of the form
事实上,R是观测到的每日降雨量时间序列。Within假设每天的降雨量是恒定的,但是between汇率突然变化的天数(即R is a 不连续的时间的函数)。我试图理解这对于解决我的常微分方程的影响。
策略1
最明显的策略(至少对我来说)是应用 SciPyodeint
在每个降雨时间步长内分别起作用。这意味着我可以治疗R作为常数。像这样的东西:
import numpy as np, pandas as pd, matplotlib.pyplot as plt, seaborn as sn
from scipy.integrate import odeint
np.random.seed(seed=17)
def f(y, t, R_t):
""" Function to integrate.
"""
# Unpack parameters
Q_t = y[0]
# ODE to solve
dQ_dt = (R_t - Q_t)/T
return dQ_dt
# #############################################################################
# User input
T = 10 # Time constant (days)
Q0 = 0. # Initial condition for outflow rate (mm/day)
days = 300 # Number of days to simulate
# #############################################################################
# Create a fake daily time series for R
# Generale random values from uniform dist
df = pd.DataFrame({'R':np.random.uniform(low=0, high=5, size=days+20)},
index=range(days+20))
# Smooth with a moving window to make more sensible
df['R'] = pd.rolling_mean(df['R'], window=20)
# Chop off the NoData at the start due to moving window
df = df[20:].reset_index(drop=True)
# List to store results
Q_vals = []
# Vector of initial conditions
y0 = [Q0, ]
# Loop over each day in the R dataset
for step in range(days):
# We want to find the value of Q at the end of this time step
t = [0, 1]
# Get R for this step
R_t = float(df.ix[step])
# Solve the ODEs
soln = odeint(f, y0, t, args=(R_t,))
# Extract flow at end of step from soln
Q = float(soln[1])
# Append result
Q_vals.append(Q)
# Update initial condition for next step
y0 = [Q, ]
# Add results to df
df['Q'] = Q_vals
策略2
第二种方法是将所有内容简单地提供给odeint
并让它处理不连续性。使用相同的参数和R值如上:
def f(y, t):
""" Function used integrate.
"""
# Unpack incremental values for S and D
Q_t = y[0]
# Get the value for R at this t
idx = df.index.get_loc(t, method='ffill')
R_t = float(df.ix[idx])
# ODE to solve
dQ_dt = (R_t - Q_t)/T
return dQ_dt
# Vector of initial parameter values
y0 = [Q0, ]
# Time grid
t = np.arange(0, days, 1)
# solve the ODEs
soln = odeint(f, y0, t)
# Add result to df
df['Q'] = soln[:, 0]
这两种方法给出了相同的答案,如下所示:
然而,第二种策略虽然代码更紧凑,但它much比第一个慢。我想这与不连续性有关R造成问题odeint
?
我的问题
- Is 策略1这里最好的方法,或者有一个更好的方法?
- Is 策略2这是一个坏主意,为什么这么慢?
谢谢你!