常微分方程(ODEs) - 有没有办法防止负值?

时间:2018-04-17 16:36:21

标签: r ode differential-equations

我正在尝试在每个空间网格单元应用常微分方程(ODE)系统。因此,每个景观单元具有相关的ODE模型。在每个时间步骤更新易感蚊子(Sv),暴露的蚊子(Se)和受感染的蚊子(St)的数量,并且ODE模型与基于离散的代理相结合动物运动模型。以下是运行ODE模型的示例:

library(deSolve)

mod1 <- function(out_tab, time_step, var){
 Sv <- out_tab[time_step,c("Sv")]
  Ev <- out_tab[time_step,c("Ev")]
  Iv <- out_tab[time_step,c("Iv")]

  Nh <- out_tab[time_step,c("Nh")]
  Ih <- out_tab[time_step,c("Ih")]
  bv <- 100
  dv <- 0.07
  betav <- 0.33
  av <- 0.5
  muv <- 0.1

  mod <- function(times, states, parameters) {
    with(as.list(c(states, parameters)), {

      dSv <- bv - dv*Sv - betav*av*(Ih/Nh)*Sv
      dEv <- betav*av*(Ih/Nh)*Sv - dv*Ev - muv*Ev
      dIv <- muv*Ev - dv*Iv

      return(list(c(dSv, dEv, dIv)))

    })
  }

  states <- c(Sv = Sv, Ev = Ev, Iv = Iv)
  input_parameters <- c(bv = bv, dv = dv, betav = betav, av = av, muv = muv)

  ## Solve ODEs
  out <- ode(func=mod, y=states, times=seq(time_step, time_step + 1, by=1), parms=input_parameters, method="iteration")
  out <- as.data.frame(out)

  out_tab[time_step + 1, c("Sv", "Ev", "Iv")] <- out[dim(out)[1], c("Sv", "Ev", "Iv")]
  # out_tab[time_step + 1, c("Nh")] <- var
  # out_tab[time_step + 1, c("Ih")] <- var

  return(out_tab)

}
out_tab <- data.frame(Sv = 10, Ev = 3, Iv = 2, Nh = 2, Ih = 1)
Nh <- c(5, 1, 8, 0, 5)
Ih <- c(2, 0, 4, 0, 1)

for(time in 1:2){
  out_tab <- mod1(out_tab = out_tab, time_step = time)
  ## print(out_tab)
  out_tab[time + 1, c("Nh")] <- Nh[time + 1]
  out_tab[time + 1, c("Ih")] <- Ih[time + 1]
}

time = 2(以天为单位),Ih(单元格中的动物数量)等于0,因此Ev的值为负数。有没有办法防止ODE的负面价值?我使用方法"iteration",因为ODE模型包含在基于代理的离散模型中。

1 个答案:

答案 0 :(得分:7)

这里有一些主要问题;长话短说你已经错误地定义了你的模型系统与method = "iteration"一起使用,所以没有多少光线修补就会得到明智的结果。我会在答案的第二部分谈到这一点,但首先,我会回答你原来的问题。

使用事件获取非负状态值

您可以使用事件强制deSolve中的非负人口规模。我建议您进一步阅读deSolve文档,因为有很多方法可以触发事件,但我们会将一个方法集成到您在每个时间步骤中触发的代码中。因为它是基于时间的,所以它需要某种最大时间来引用,因此我创建了一个maxtime值,您也可以在for循环中使用它。您应该在其他函数可访问的位置定义此值;我只是把它放在你的mod1函数声明之前。

# Here we declare the maximum time to which your system will evaluate
maxtime <- 2

# This is where we define your event function
# Add this directly above your call to ode()
  posfun <- function(t, y, parms){
    with(as.list(y), {
      y[which(y<0)] <- 0  
      return(y)
    })
  }

# Here's your original call to ode(), with a small addition
# Notice that we added events; iteration is missing, more on that later
  out <- ode(func=mod, 
             y=states, 
             times=seq(time_step, time_step + 1, by=1), 
             parms=input_parameters, 
             events=list(func = posfun, time = c(0:maxtime)))

  out <- as.data.frame(out)

# Don't forget to add maxtime to your for-loop
for(time in 1:maxtime){
  out_tab <- mod1(out_tab = out_tab, time_step = time)
  ## print(out_tab)
  out_tab[time + 1, c("Nh")] <- Nh[time + 1]
  out_tab[time + 1, c("Ih")] <- Ih[time + 1]

查看posfun,我们看到它只是在每个时间步检查每个状态变量,并将任何负值设置为零。如果我们检查输出,我们会看到它给出了非负人口密度:

 out_tab
        Sv       Ev       Iv Nh Ih
1  10.0000  3.00000 2.000000  2  1
3 179.7493 16.67784 3.244288  1  0
4 416.2958 10.01499 6.133576  8  4

很好,对吗?嗯,不太好。不幸的是,目前无法在method = iteration时使用事件。鉴于到目前为止您已经分享了关于模型的内容,我们肯定会在连续时间内对其进行建模。仅仅因为分散事件被离散化并不一定意味着出生,死亡和感染事件也必须是离散的。重要的是要概念化这些现象在现实生活中发生的不同时间尺度,并问问自己是否使用模型准确捕捉它们。但是,这已经超出了StackOverflow的范围,所以在第二部分:

使用deSolve将离散时间模型编码为R

查看iterationdeSolve方法的文档,我们看到:&#34; Method&#34; iteration&#34;特别之处在于函数func应该返回状态变量的新值而不是变化率。&#34;

您已经在连续时间模型中清楚地编码,该模型返回衍生物的值,而不是返回状态变量的值的离散时间模型。你dSv中的出生成分是一个常数,所以你使用的是一个内在的出生;在一个离散时间模型中,你的出生成分将是后代的一些常数(几乎总是一个整数)乘以&#34;父母的数量&#34;从上一个时间步。

观察你的方程组,我们可以看出这是如何产生问题的:你不是每个Sv个人在每个时间步产生100个人,而是设置< / em> Sv到100。 Sv不可避免地会在你快速累积净亏损时直线下跌。

示例离散时间模型如下所示:

# discrete-time host-parasite model
Parasite <- function(t, y, ks) {
P <- y[1]
H <- y[2]
f <- A * P / (ks + H)
Pnew <- H * (1 - exp(-f))
Hnew <- H * exp(rH * (1 - H) - f)
list (c(Pnew, Hnew))
}

请注意我们如何不断提及&#34;现在&#34;为了计算下一时间步的种群动态,P和H的值。我希望这对你有所帮助,祝你在模特冒险中好运!