我正在使用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")
答案 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
的“黄金”解决方案进行比较。这给出了覆盖的解决方案路径
曲线上没有可见差异。对于零部件本身及其误差除以公差值,我得到了图
同样,在第一张图中,三种解决方案的覆盖范围也没有明显差异。比例误差图显示了与解决方案的快速变化相对应的大振荡。