Pyomo.dae中基于根查找的事件(Events)实现方法咨询
在Pyomo中实现DAE模型的事件(根查找)机制
Pyomo.dae本身没有直接封装Matlab式的事件触发重启逻辑,但可以通过分段积分+根检测的方式实现,结合CasADi的原生根查找能力或手动分段求解来适配你的需求。以下是两种可行方案:
方案1:利用CasADi的根查找功能(推荐)
由于你计划通过CasADi调用IDAS,CasADi原生支持定义根函数,当函数值为0时触发事件,中断积分并处理状态更新,效率更高。
步骤示例
定义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}定义根函数与配置积分器
根函数返回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)循环积分+事件处理
捕获事件触发信号,更新状态后重启积分:# 初始化参数 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
相关产品推荐
相关产品推荐

