如何在DifferentialEquations.jl中高效获取额外变量d、e、f的输出?
解决DifferentialEquations.jl中获取额外变量的问题
方法一:使用回调函数(推荐用于指定时间点记录)
无需修改原ODE函数,通过SavedValues和SavingCallback安全记录额外变量,规避全局变量和时间点匹配问题:
using DifferentialEquations # 原ODE函数保持不变 function test1(du,u,p,t) a,b,c = p d=a^0.1*(t+1) e=u[1]/a f=u[2]/d du[1] = a*u[1] du[2] = d*u[2] du[3] = b*u[2] - c*u[3] end # 定义生成额外变量的函数 function save_vars(u,t,p) a,b,c = p d = a^0.1*(t+1) e = u[1]/a f = u[2]/d return (d=d, e=e, f=f) # 返回命名元组,方便后续按名称访问 end p = (2,0.75,0.8) u0 = [1.0;1.0;1.0] tspan = (0.0,3.0) prob = ODEProblem(test1,u0,tspan,p) # 指定要保存的时间点 sampletimes = tspan[1]:0.3:tspan[2] # 初始化存储结构 saved_values = SavedValues(Float64, NamedTuple{(:d,:e,:f), Tuple{Float64,Float64,Float64}}) # 创建回调 cb = SavingCallback(save_vars, saved_values, saveat=sampletimes) # 求解过程,无需重复设置saveat(若需要状态变量同步在这些点输出,可添加saveat=sampletimes) sol = solve(prob, callback=cb) # 访问保存的结果 saved_d = saved_values.saveval.d saved_e = saved_values.saveval.e saved_f = saved_values.saveval.f # 对应的时间点为saved_values.t
该方法优势:
- 无全局变量,线程安全且代码可维护性高
- 精确匹配指定保存时间点,避免浮点数精度导致的漏记/重复记录
- 支持灵活的变量存储格式(元组、数组、字典均可)
方法二:后处理计算(适配所有求解时间点)
若需要在全部求解生成的时间点(包括默认输出的所有点)计算额外变量,直接利用sol的t和u数组批量计算即可,无需修改ODE函数或使用回调:
using DifferentialEquations # 原ODE函数和求解流程不变 function test1(du,u,p,t) a,b,c = p d=a^0.1*(t+1) e=u[1]/a f=u[2]/d du[1] = a*u[1] du[2] = d*u[2] du[3] = b*u[2] - c*u[3] end p = (2,0.75,0.8) u0 = [1.0;1.0;1.0] tspan = (0.0,3.0) prob = ODEProblem(test1,u0,tspan,p) sol = solve(prob) # 不设置saveat,获取求解器默认输出的全部时间点 # 批量计算额外变量 a,b,c = p d = @. a^0.1*(sol.t + 1) e = sol[1,:] ./ a # sol[1,:]直接获取所有时间点的u[1] f = sol[2,:] ./ d # sol[2,:]直接获取所有时间点的u[2]
该方法优势:
- 实现最简单,无需额外逻辑
- 自动适配所有求解时间点,无论是默认输出还是
saveat指定的点 - 利用广播机制批量计算,性能高效
原方案的问题说明
- 时间点匹配误差:
t in sampletimes依赖浮点数精确相等,但求解器实际时间点会因步长调整存在精度偏差,导致漏记或重复记录 - 全局变量风险:全局变量
i、Out_values会引发线程安全问题,且代码可维护性差 - 重复设置的冲突:同时设置
saveat和tstops会导致求解器内部处理时间点时出现微小差异——tstops是强制停止点,saveat是插值生成点,二者数值可能不完全一致
内容的提问来源于stack exchange,提问作者eod
相关产品推荐
相关产品推荐

