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

Pyomo索引块与标准建模性能对比咨询:大规模时序能源优化

在Pyomo大规模时序优化问题中,标准建模是否比索引块建模更快?

我正在用Pyomo构建含大量时段(如8760小时)的时序能源优化模型,参考《Pyomo Book》8.6.2节的索引块方式,为每个时段创建独立Block。但实际测试发现,索引块建模的耗时远高于无块的标准建模,且模型越复杂差距越大。以下是光伏-电池系统成本最小化的两种实现代码,逻辑完全一致,但索引块建模的建模耗时约为标准建模的4倍。

标准建模实现

import pyomo.environ as pyo
from pyomo.common.timing import TicTocTimer
import random

# 生成PV和负荷随机数据
pv = [random.randint(0, 5) for _ in range(8760)]
pv_dict = dict(enumerate(pv, 1))

load_el = [random.randint(0, 8) for _ in range(8760)]
load_el_dict = dict(enumerate(load_el, 1))

# 创建标准形式Pyomo模型
timer = TicTocTimer()
timer.tic('Start Sandard formulation')

model = pyo.ConcreteModel()

# 定义时段集合
model.T = pyo.RangeSet(len(pv_dict))
# 定义参数
model.pv = pyo.Param(model.T, initialize=pv_dict)
model.load_el = pyo.Param(model.T, initialize=load_el_dict)
model.grid_cost_buy = pyo.Param(model.T, initialize=0.4)
model.battery_eoCH = pyo.Param(initialize=1.0)
model.battery_eoDCH = pyo.Param(initialize=0.1)
model.battery_capacity = pyo.Param(initialize=5)

# 定义变量
model.battery_power_CH = pyo.Var(model.T, domain=pyo.NonNegativeReals)         # 电池充电功率
model.battery_power_DCH = pyo.Var(model.T, domain=pyo.NonNegativeReals)        # 电池放电功率
model.battery_soc = pyo.Var(model.T, bounds=(model.battery_eoDCH, model.battery_eoCH))     # 电池SOC
model.grid_power_import = pyo.Var(model.T, domain=pyo.NonNegativeReals)        # 电网购电功率
model.grid_power_export = pyo.Var(model.T, domain=pyo.NonNegativeReals)        # 电网售电功率

# 电池约束
def battery_end_of_CH_rule(m, t):
    return m.battery_soc[t] <= model.battery_eoCH
model.battery_eoCH_c = pyo.Constraint(model.T, rule=battery_end_of_CH_rule)

def battery_end_of_DCH_rule(m, t):
    return m.battery_soc[t] >= model.battery_eoDCH
model.battery_eoDCH_c = pyo.Constraint(model.T, rule=battery_end_of_DCH_rule)

def battery_soc_rule(m, t):
    if t == m.T.first():
        return m.battery_soc[t] == ((m.battery_power_CH[t] - m.battery_power_DCH[t]) / model.battery_capacity)
    return m.battery_soc[t] == m.battery_soc[t-1] + ((m.battery_power_CH[t] - m.battery_power_DCH[t]) / model.battery_capacity)
model.battery_soc_c = pyo.Constraint(model.T, rule=battery_soc_rule)

# 电力平衡约束
def balanced_bus_rule(m, t):
    return (0 == (m.pv[t] - m.load_el[t] 
                 + m.battery_power_DCH[t] - m.battery_power_CH[t]
                 + m.grid_power_import[t] - m.grid_power_export[t]))
model.bus_c = pyo.Constraint(model.T, rule=balanced_bus_rule)

# 目标函数
def obj_rule(m):
    return sum(m.grid_power_import[t]*m.grid_cost_buy[t] for t in m.T)
model.obj = pyo.Objective(rule=obj_rule, sense=1)
timer.toc('Built model')

# 求解
solver = pyo.SolverFactory('gurobi')
results = solver.solve(model)
timer.toc('Wrote LP file and solved')
print('Total operation costs:', pyo.value(model.obj))

索引块建模实现

import pyomo.environ as pyo
from pyomo.common.timing import TicTocTimer
import random

# 生成PV和负荷随机数据
pv = [random.randint(0, 5) for _ in range(8760)]
pv_dict = dict(enumerate(pv, 1))

load_el = [random.randint(0, 8) for _ in range(8760)]
load_el_dict = dict(enumerate(load_el, 1))

# 创建索引块形式Pyomo模型
timer = TicTocTimer()
timer.tic('Start Indexed Block formulation')

model = pyo.ConcreteModel()
model.T = pyo.RangeSet(len(pv_dict))

def block_rule(b, t):
    # 定义参数
    b.pv = pyo.Param(initialize=pv_dict[t])
    b.load_el = pyo.Param(initialize=load_el_dict[t])
    b.grid_cost_buy = pyo.Param(initialize=0.4)
    b.battery_eoCH = pyo.Param(initialize=1.0)
    b.battery_eoDCH = pyo.Param(initialize=0.1)
    b.battery_capacity = pyo.Param(initialize=5)    
    # 定义变量
    b.battery_power_CH = pyo.Var(domain=pyo.NonNegativeReals)              # 电池充电功率
    b.battery_power_DCH = pyo.Var(domain=pyo.NonNegativeReals)             # 电池放电功率
    b.battery_soc = pyo.Var(bounds=(b.battery_eoDCH, b.battery_eoCH))          # 电池SOC
    b.battery_soc_initial = pyo.Var()                                           # 初始SOC
    b.grid_power_import = pyo.Var(domain=pyo.NonNegativeReals)             # 电网购电功率
    b.grid_power_export = pyo.Var(domain=pyo.NonNegativeReals)             # 电网售电功率 

    # 约束定义
    def balanced_bus_rule(_b):
        return (0 == (_b.pv - _b.load_el
                      + _b.battery_power_DCH - _b.battery_power_CH
                      + _b.grid_power_import - _b.grid_power_export))
    b.bus_c = pyo.Constraint(rule=balanced_bus_rule)
    
    def battery_end_of_CH_rule(_b):
        return (_b.battery_eoCH >= _b.battery_soc)
    b.battery_eoCH_c = pyo.Constraint(rule=battery_end_of_CH_rule)
    
    def battery_end_of_DCH_rule(_b):
        return (_b.battery_eoDCH <= _b.battery_soc)
    b.battery_eoDCH_c = pyo.Constraint(rule=battery_end_of_DCH_rule)
    
    def battery_soc_rule(_b):
        return (_b.battery_soc == _b.battery_soc_initial + ((_b.battery_power_CH - _b.battery_power_DCH) / _b.battery_capacity))
    b.battery_soc_c = pyo.Constraint(rule=battery_soc_rule)
        
# 初始化每个时段的Block
model.pvbatb = pyo.Block(model.T, rule=block_rule)

# 跨块SOC链接约束
def battery_soc_linking_rule(m, t):
    if t == m.T.first():
        return m.pvbatb[t].battery_soc_initial == 0
    return m.pvbatb[t].battery_soc_initial == m.pvbatb[t-1].battery_soc
model.battery_soc_linking = pyo.Constraint(model.T, rule=battery_soc_linking_rule)

# 目标函数
def obj_rule(m):
    return sum(m.pvbatb[t].grid_power_import * m.pvbatb[t].grid_cost_buy for t in m.T)
model.obj = pyo.Objective(rule=obj_rule, sense=1)
timer.toc('Built model')

# 求解
solver = pyo.SolverFactory('gurobi')
results = solver.solve(model)
timer.toc('Wrote LP file and solved')
print('Total operation costs:', pyo.value(model.obj))

测试输出

[    0.00] Start Sandard formulation
[+   0.92] Built model
[+   3.37] Wrote LP file and solved
Total operation costs: 5706.199999999935
[    4.31] Start Indexed Block formulation
[+  12.50] Built model
[+   4.62] Wrote LP file and solved
Total operation costs: 5706.199999999934

回答

结论:是的,大规模时序优化场景下标准建模通常比索引块建模更快

核心原因

  1. 对象创建开销差异:标准建模通过集合索引一次性创建批量的Param、Var、Constraint对象,Pyomo内部采用更高效的数组式管理;而索引块为每个时段创建独立Block,每个Block内又重复创建大量独立对象,8760个时段会带来8760倍的单个对象初始化开销,累计后差距显著。
  2. 跨时段约束的额外成本:索引块建模需要额外定义跨块的链接约束(如SOC时序衔接),这部分逻辑需要遍历所有Block并建立关联,比标准建模中直接通过t-1索引关联的方式更耗时。
  3. Pyomo内部处理复杂度:嵌套的Block结构会增加Pyomo遍历模型组件、生成LP文件时的复杂度,尤其是在大规模场景下,这种嵌套层级会拖慢模型预处理速度。

索引块建模的适用场景

尽管性能劣势明显,索引块仍有其价值:

  • 模块化复用:当需要重复使用时段子模型逻辑(如多场景扩展、多设备类型建模),Block的封装能提升代码可维护性。
  • 复杂子模型管理:若每个时段的子模型包含大量独立逻辑(如多设备耦合、多约束组),用Block封装可让代码结构更清晰,此时性能开销可能在可接受范围内。

索引块建模的优化建议

如果必须使用索引块,可尝试以下方式降低开销:

  • 共享全局参数:将电池容量、充放电效率等全局参数定义在顶层模型中,Block内部直接引用,避免重复创建大量相同参数对象。
  • 简化链接约束:尽量采用批量方式定义跨块约束,减少循环遍历的开销。
  • 使用轻量Block:用pyomo.core.base.block.SimpleBlock替代默认Block,它去掉了一些高级功能,初始化开销更低。

内容的提问来源于stack exchange,提问作者fabmid

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:18:04