为什么在R中使用事件函数的ODE解决方案有所不同?

时间:2019-05-02 22:27:16

标签: r ode

我正在使用R作为当前项目的一部分,解决一个复杂的非线性ODE系统。在测试阶段,我通过两种方式使用lsoda方法实现了解决方案(a)使用事件函数(b)不使用事件函数。两种解决方案产生的结果都略有不同。 我不明白为什么会发生这种情况或如何最大程度地减少错误?

我通过这两种方法尝试了Lotka–Volterra方程,但仍然想知道为什么解中存在差异(请参见附图)。尽管差异很小,但我想将对我的项目可能很大的错误减至最少。如您所见,事件函数不会更改因变量,但仍会产生一些错误。

rm(list=ls())

library(deSolve);
#############################################################
predpreyLV<-function(t,y,p){
N<-y[1]
P<-y[2]

with(as.list(p),{
   dNdt<- r*N*(1-(N/1000))-a*P*N
   dPdt<- -b*P+f*P*N
   return(list(c(dNdt,dPdt)))
  })
}
#############################################################
eventFun<-function(t,y,p){
   return (y)
}
#############################################################

r<-0.5; a<-0.01; f<-0.01; b<-0.2;

# Simulation without using event function
p<-c(r=r,a=a, b=b, f=f)
y0<-c(N=25, P=5)
times<-seq(0,1000,0.1)
out1<-ode(y=y0,times,predpreyLV, p,method="lsoda", atol=1e-6, rtol=1e-6)

# Simulation using event function
p<-c(r=r,a=a, b=b, f=f)
y0<-c(N=25, P=5)
times<-seq(0,1000,0.1)
out2<-ode(y=y0,times,predpreyLV, p,method="lsoda",
             events=list(func=eventFun, time=times), atol=1e-6, rtol=1e-6)

plot(out1[,1],abs(out1[,2]-out2[,2]), type="l", ylab="Difference in size")

enter image description here

1 个答案:

答案 0 :(得分:1)

您必须考虑到,如果没有特别说明,则所有求解器都具有自适应步长。这意味着求解器将使用用户通常看不见的内部步骤,并在内部节点之间使用特定的多项式插值来生成用户请求的值。

但是,由于事件可以更改状态,因此求解器无法从下一个内部节点继续进行,它必须从事件时间重新开始求解过程,并将事件函数返回的新状态作为初始状态。

这有几种效果。该事件之前的最后一步被缩短。重新启动过程可能使用较小的非最佳步长。 lsoda中使用的多步方法的初始化使用了另一种方法,可能也采用了非典型的步长。 lsoda还调整了多步方法的顺序。这也必须凭经验确定,使求解器的步长低于最佳步长。

所有这些虽然并没有太大地改变解决方案的准确性,但在步长和方法/顺序方面仍然导致了不同的集成路径。如果系统具有刚性区域(如多项式非线性右侧的情况),则这些小的变化可以放大到观察到的比例。

相对于1000的最大大小N,误差0.3的相对大小为3e-5,在给定公差的预期范围内。


为模拟不同积分控制过程的效果,我使用绝对和相对公差参数{{来运行给定系统(使用python-scipy的solve_ivp和方法“ LSODA”)针对不同的公差输入tol in 1e-6, 1e-8, 1e-10 1}}。然后将这些与公差为atol=1e-2*tol, rtol=tol的“黄金”解决方案进行比较。这给出了覆盖的解决方案路径

enter image description here

曲线上没有可见差异。对于零部件本身及其误差除以公差值,我得到了图

enter image description here

同样,在第一张图中,三种解决方案的覆盖范围也没有明显差异。比例误差图显示了与解决方案的快速变化相对应的大振荡。