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

Pyomo.dae中基于根查找的事件(Events)实现方法咨询

在Pyomo中实现DAE模型的事件(根查找)机制

Pyomo.dae本身没有直接封装Matlab式的事件触发重启逻辑,但可以通过分段积分+根检测的方式实现,结合CasADi的原生根查找能力或手动分段求解来适配你的需求。以下是两种可行方案:

方案1:利用CasADi的根查找功能(推荐)

由于你计划通过CasADi调用IDAS,CasADi原生支持定义根函数,当函数值为0时触发事件,中断积分并处理状态更新,效率更高。

步骤示例

  1. 定义Pyomo DAE模型并转换为CasADi符号系统
    先在Pyomo中完成DAE模型定义,再将状态变量、代数变量和残差映射到CasADi的符号表达式:

    import pyomo.environ as pyo
    from pyomo.dae import ContinuousSet, DerivativeVar
    import casadi as ca
    
    # 1. 构建Pyomo DAE模型
    m = pyo.ConcreteModel()
    m.t = ContinuousSet(bounds=(0, 10))
    m.x = pyo.Var(m.t)  # 状态变量
    m.dxdt = DerivativeVar(m.x, wrt=m.t)
    m.y = pyo.Var(m.t)  # 代数变量
    
    # DAE残差方程
    def dae_rule(m, t):
        return m.dxdt[t] == -m.x[t] + m.y[t]
    m.dae_con = pyo.Constraint(m.t, rule=dae_rule)
    
    # 代数约束(示例:依赖状态变量的分支逻辑)
    def alg_rule(m, t):
        return m.y[t] == pyo.sin(t) if m.x[t] < 0 else pyo.cos(t)
    m.alg_con = pyo.Constraint(m.t, rule=alg_rule)
    
    # 2. 转换为CasADi符号模型
    t_ca = ca.SX.sym('t')
    x_ca = ca.SX.sym('x')
    y_ca = ca.SX.sym('y')
    dxdt_ca = ca.SX.sym('dxdt')
    
    # 映射残差表达式
    dae_res = dxdt_ca + x_ca - y_ca
    alg_res = y_ca - (ca.sin(t_ca) if x_ca < 0 else ca.cos(t_ca))
    
    # 构建CasADi DAE系统描述
    dae_system = {'x':x_ca, 'z':y_ca, 't':t_ca, 'ode':dae_res, 'alg':alg_res}
    
  2. 定义根函数与配置积分器
    根函数返回0时触发事件,配置CasADi的IDAS积分器启用根检测:

    # 根函数:示例为x(t)=0时触发事件
    root_func = x_ca
    
    # 创建带根查找的IDAS积分器
    integrator_opts = {'rootfinder':'idas', 'roots':1}  # roots指定根的数量
    integrator = ca.integrator('idas_integrator', 'idas', dae_system, integrator_opts)
    
  3. 循环积分+事件处理
    捕获事件触发信号,更新状态后重启积分:

    # 初始化参数
    t_current = 0.0
    x_current = 1.0  # 初始状态
    y_current = ca.cos(t_current)  # 初始代数变量值
    t_final = 10.0
    simulation_results = {'t':[t_current], 'x':[x_current], 'y':[y_current]}
    
    while t_current < t_final:
        # 积分到下一个事件或终点
        res = integrator(x0=x_current, z0=y_current, t0=t_current, tf=t_final)
        t_next = res['tf'].full()[0][0]
        x_next = res['xf'].full()[0][0]
        y_next = res['zf'].full()[0][0]
        root_triggered = res['root'].full()[0][0]  # 1表示事件触发,0表示到达终点
    
        # 记录当前段结果
        simulation_results['t'].append(t_next)
        simulation_results['x'].append(x_next)
        simulation_results['y'].append(y_next)
    
        if root_triggered:
            # 事件处理:按你的逻辑更新起始状态(示例反转x符号)
            x_current = -x_next
            y_current = ca.sin(t_next)  # 同步更新代数变量
            t_current = t_next
        else:
            break
    

方案2:纯Pyomo手动分段积分

如果不想依赖CasADi的根查找,可以手动分段定义DAE区间,通过检测根函数的符号变化定位事件点,再重启积分。

步骤示例

import pyomo.environ as pyo
from pyomo.dae import ContinuousSet, DerivativeVar, TransformationFactory
import numpy as np

# 定义Pyomo DAE模型(同方案1)
m = pyo.ConcreteModel()
m.t = ContinuousSet(bounds=(0, 10))
m.x = pyo.Var(m.t)
m.dxdt = DerivativeVar(m.x, wrt=m.t)
m.y = pyo.Var(m.t)

def dae_rule(m, t):
    return m.dxdt[t] == -m.x[t] + m.y[t]
m.dae_con = pyo.Constraint(m.t, rule=dae_rule)

def alg_rule(m, t):
    return m.y[t] == pyo.sin(t) if m.x[t] < 0 else pyo.cos(t)
m.alg_con = pyo.Constraint(m.t, rule=alg_rule)

# 根函数表达式
m.root_expr = pyo.Expression(m.t, rule=lambda m, t: m.x[t])

# 初始条件与求解器
m.x[0] = 1.0
m.y[0] = pyo.cos(0)
solver = pyo.SolverFactory('casadi')
sim_results = {'t':[0.0], 'x':[1.0], 'y':[1.0]}

t_current = 0.0
t_final = 10.0
step_size = 0.5  # 初始积分步长

while t_current < t_final:
    # 设置当前积分区间
    current_end = min(t_current + step_size, t_final)
    m.t = ContinuousSet(bounds=(t_current, current_end))
    
    # 应用配点法离散化
    discretizer = TransformationFactory('dae.collocation')
    discretizer.apply_to(m, nfe=5, ncp=3)
    
    # 求解当前段
    solver.solve(m)
    
    # 提取区间内的根函数值,检查是否触发事件
    t_points = list(m.t)
    root_vals = [pyo.value(m.root_expr[t]) for t in t_points]
    event_idx = None
    for i in range(len(root_vals)-1):
        if root_vals[i] * root_vals[i+1] <= 0:
            event_idx = i
            break
    
    if event_idx is not None:
        # 二分法精确定位事件点
        t_low = t_points[event_idx]
        t_high = t_points[event_idx+1]
        for _ in range(10):
            t_mid = (t_low + t_high)/2
            m.t = ContinuousSet(bounds=(t_current, t_mid))
            discretizer.apply_to(m, nfe=1, ncp=3)
            solver.solve(m)
            root_mid = pyo.value(m.root_expr[t_mid])
            if root_mid * root_vals[event_idx] <= 0:
                t_high = t_mid
            else:
                t_low = t_mid
        t_event = (t_low + t_high)/2
        x_event = pyo.value(m.x[t_event])
        y_event = pyo.value(m.y[t_event])
        
        # 记录事件点结果
        sim_results['t'].append(t_event)
        sim_results['x'].append(x_event)
        sim_results['y'].append(y_event)
        
        # 更新起始状态,重启积分
        t_current = t_event
        m.x[t_current] = -x_event  # 示例状态更新逻辑
        m.y[t_current] = pyo.sin(t_current)
        step_size = 0.5
    else:
        # 无事件,记录区间终点状态
        x_end = pyo.value(m.x[current_end])
        y_end = pyo.value(m.y[current_end])
        sim_results['t'].append(current_end)
        sim_results['x'].append(x_end)
        sim_results['y'].append(y_end)
        t_current = current_end
        step_size = min(step_size * 1.2, 1.0)  # 自适应步长

关键注意事项

  • CasADi方案效率更高:对于频繁触发事件的模型,CasADi原生根查找比手动分段更高效,且支持复杂根函数。
  • 状态更新一致性:事件触发后的状态/代数变量更新逻辑必须与Matlab版本严格对齐,避免仿真偏差。
  • 版本兼容性:确保Pyomo与CasADi版本匹配,CasADi需支持IDAS根查找功能。

内容的提问来源于stack exchange,提问作者François LESAGE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 08:23:10