使用时间序列参数来解决R中的ODE

时间:2014-02-04 16:15:54

标签: r ode differential-equations

我正在尝试使用deSolve在R中解决一个简单的ODE:dQ/dt = f(Q)*(P - E).整个事情是Q的时间序列。诀窍是P和E是固定的时间序列数据本身,所以diff eq仅在Q中有效。

使用固定的时间步长迭代地解决这个问题相对简单,但我试图找到一种在R中使用自适应时间步长的方法。因为P和E是离散的,连续的时间步长可能具有相同的P值和E,这很好。我一直在玩deSolve,但一直无法解决这个问题。理想情况下,我想使用标准的4阶Runge-Kutta。

有什么想法吗?在MATLAB中做到吗?

编辑可重复的示例。我希望能够使用可变时间步Runge-Kutta 4方法进行此计算。我可以很容易地编写固定时间步rk4,而不是那么自适应。

working <- structure(list(datetime = structure(c(1185915600, 1185919200, 
1185922800, 1185926400, 1185930000, 1185933600, 1185937200, 1185940800, 
1185944400, 1185948000, 1185951600), class = c("POSIXct", "POSIXt"
), tzone = "UTC"), p = c(0, 0, 0, 1.1, 0.5, 0.7, 0, 0, 1.3, 0, 
0), e = c(0.15, 0.14, 0.13, 0.21, 0.15, 0.1, 0.049, 0, 0, 0, 
0), qsim = c(-1.44604436552566, NA, NA, NA, NA, NA, NA, NA, NA, 
NA, NA)), .Names = c("datetime", "p", "e", "qsim"), row.names = c(NA, 11L), 
class = "data.frame")


# this is the derivative function dQ/dt = f(Q,p,e) where p and e are time series
dqdt <- function(qsim, pcp, pet) {
  dq <- (qsim ^ 2) * ((pcp - pet) / qsim) 
  return(dq)
}

# loops through and calculates the Q at each time step using the Euler method
for (i in 1:(nrow(working) - 1)) {
  dq <- dqdt(working$qsim[i], pcp = working$p[i], pet = working$e[i])
  working$qsim[i + 1] <- working$qsim[i] + dq
}

3 个答案:

答案 0 :(得分:2)

可能不是最复杂的方法,但快速而肮脏的方法是将依赖于时间的强制变量保持为外部(全局)变量。

我使用pmax(1,ceiling(t))从时间索引转换为数据框的行索引(如果您想从pmax开始,则需要t=0,因为ceiling(0)是0,R中的x[0]通常是一个空向量,然后破坏东西)。可能还有其他方法可以进行索引。

library(deSolve)
gradfun <- function(t,y,parms) {
   pcp <- working$p[pmax(1,ceiling(t))]
   pet <- working$e[pmax(1,ceiling(t))]
   list(y^2*((pcp-pet)/y),NULL)
}

gradfun(0,working$qsim[1],1)   ## test
ode1 <- ode(y=c(qsim=working$qsim[1]),func=gradfun,
                time=seq(0,nrow(working),length.out=101),
                parms=NULL,method="rk4")
plot(ode1)

enter image description here

答案 1 :(得分:0)

另一种方法是开发一个函数来描述依赖于时间的强制变量。如果值之间的线性插值是正确的,请使用类似:

pcp_F <- approxfun(0:nrow(working), c(0, working$p ), method = "linear")
pet_F <- approxfun(0:nrow(working), c(0.150, working$e ), method = "linear")

或者,将方法更改为“常量”,但测试您使用的是给定时间索引所期望的值。

parameters <- NULL
state <- c(qsim = working$qsim[1])
times <- seq(0,nrow(working),length.out=101)

gradfun <- function(t, state, parameters) {
  with(as.list(c(state, parameters)),{
  # rate of change

  pcp <- pcp_F(t)
  pet <- pet_F(t)
  dq <- (qsim ^ 2) * ((pcp - pet) / qsim) 
  list(dq)
  })
}

out_de <- ode(y = state, times = times, func = gradfun, parms = parameters, method = "rk4")
plot(out_de)

答案 2 :(得分:0)

Ben的解决方案可以通过保存你的向量工作$ p并在“参数”列表中工作$ e来改进,该列表将传递给ODE。 而不是参数&lt; - NULL,请参数2&lt; - list(vec1 = your_time_series,vec2 = your_other_time_series)。然后:

gradfun <- function(t,y,parms) {
   pcp <- vec1[pmax(1,ceiling(t))]
   pet <- vec2[pmax(1,ceiling(t))]
   list(y^2*((pcp-pet)/y),NULL)
}

这样你就不必在外部(全局)变量中传递,这可能是一个巨大的痛苦