OrdinaryDiffEq积分中用回调修改问题与向量尺寸却保留原解向量
在OrdinaryDiffEq中通过回调扩展解向量并修改问题的正确方法
你的问题核心是:回调函数中重新赋值integrator只是局部变量,不会改变外部正在运行的积分器实例,因此原积分器仍会继续求解原问题。以下是两种可行的修正方案:
方案一:直接修改现有积分器的内部状态
这种方法无需重新创建积分器,直接调整当前积分器的右端函数、解向量及缓存,效率更高:
using OrdinaryDiffEq function modify_integrator!(integrator) # 扩展解向量 new_u = [integrator.u; 1.0] # 定义新的ODE右端函数 function new_growth!(du, u, p, t) println("new_growth! called") du[1] = 0.1*u[1] du[2] = 0.2*u[2] du[3] = 0.3*u[3] end # 更新积分器的右端函数 integrator.f = ODEFunction(new_growth!) # 调整解向量及相关缓存的大小并赋值 resize!(integrator.u, 3) integrator.u .= new_u resize!(integrator.uprev, 3) integrator.uprev .= new_u resize!(integrator.cache.du, 3) resize!(integrator.cache.u, 3) end # 原ODE右端函数 function growth!(du, u, p, t) du[1] = 0.1*u[1] du[2] = 0.2*u[2] end # 初始条件与时间区间 u0 = [1.0; 1.0] tspan = (0.0, 10.0) # 定义回调:t>9时触发修改 condition(u, t, integrator) = t > 9 cb = DiscreteCallback(condition, modify_integrator!, save_positions=(false,false)) callbacks = CallbackSet(cb) prob = ODEProblem(growth!, u0, tspan, callback=callbacks) sol = solve(prob, Tsit5()) # 验证最终解的分量数 println(length(sol[end])) # 输出3,符合预期
方案二:使用reinit!重置积分器
通过reinit!直接修改当前积分器实例,而非创建局部变量。需注意避免回调的循环引用问题:
using OrdinaryDiffEq # 提前声明回调变量,避免循环引用 cb = nothing function modify_integrator!(integrator) new_u = [integrator.u; 1.0] tspan_new = (integrator.t, 10.0) # 定义新的ODE问题 new_prob = ODEProblem((du,u,p,t)->begin println("new_growth! called") du[1] = 0.1*u[1] du[2] = 0.2*u[2] du[3] = 0.3*u[3] end, new_u, tspan_new, callback=cb) # 重置当前积分器,保留原步长 reinit!(integrator, new_prob; dt=integrator.dt, save_start=false) end # 原ODE右端函数 function growth!(du, u, p, t) du[1] = 0.1*u[1] du[2] = 0.2*u[2] end # 初始条件与时间区间 u0 = [1.0; 1.0] tspan = (0.0, 10.0) # 定义回调 condition(u, t, integrator) = t > 9 cb = DiscreteCallback(condition, modify_integrator!, save_positions=(false,false)) callbacks = CallbackSet(cb) prob = ODEProblem(growth!, u0, tspan, callback=callbacks) sol = solve(prob, Tsit5()) # 验证最终解的分量数 println(length(sol[end])) # 输出3,符合预期
关键注意点
- 不要在回调中重新赋值
integrator局部变量,这不会影响外部正在运行的积分器实例。 - 方案一更适合轻量修改,避免重新初始化的开销;方案二适合完全替换问题场景。
- 若新问题无需继续触发回调,可在创建
new_prob时去掉callback=cb参数,避免不必要的循环调用。
内容的提问来源于stack exchange,提问作者SimonCan
相关产品推荐
相关产品推荐

