You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:15:59