干这行这些年,经常有朋友拿着综合能源系统优化规划的问题来找我,聊不了几句就会发现卡在同一个地方:模型建出来了,目标函数、约束条件一大片,可一到求解就原形毕露,要么内存扛不住,要么求解器转了几个小时都不动弹。这时候我都会问一句,试过广义Benders分解(GBD)没有?大部分人的反应是:听说过,但不知道代码怎么写,也不知道写出来到底该怎么验证。
这篇文章就把我手头这套“基于广义Benders分解的综合能源系统优化规划代码”彻底拆开来讲。它解决的不是某个神仙算法问题,而是实打实的工程问题:在设备选型、容量配置这类离散决策和运行调度这类连续优化耦合在一起时,怎么把一个大到没法直接啃的优化模型,切成主问题和子问题交替求解,让求解器只干自己擅长的事。这套代码适合正在做园区综合能源、区域多能互补规划的研究生,也适合想往优化算法方向转型的工程师。你看完至少要搞明白三件事:代码里每个模块在干什么、主问题和子问题之间靠什么信息传递、以及迭代半天还不收敛时该从哪里下手排查。
1. 为什么综合能源系统优化规划最终落到GBD
1.1 规划问题到底长什么样
先把这个问题的物理本质说清楚。所谓综合能源系统优化规划,本质上是在回答几个连在一起的问题:这片园区到底要装光伏吗?装多大?燃气轮机选哪个型号?电制冷机和溴化锂机组怎么搭配?蓄电和蓄热罐要不要上?这些问题一旦定了,系统在典型日里怎么运行、每小时各设备出多少力,又是一个完整的最优调度问题。更麻烦的是,投资决策和运行决策是互相咬合的——设备容量选大了,投资成本高但运行灵活;选小了,运行阶段可能根本满足不了负荷需求。所以这类问题通常写成混合整数线性规划(MILP)或混合整数非线性规划(MINLP),决策变量里既有表示“建或不建”的0-1变量,又有表示“出力多少”的连续变量。
用一个生活化的类比帮助理解:你装修房子,要决定装不装地暖、装几台空调,这是离散决策;而装完之后每个月怎么用电、烧多少气,这是连续决策。最优方案不能让两者各算各的,必须放在一个框架里同时考虑。综合能源系统规划就是在这种“投资+运行”双层耦合结构里找全局最优。
1.2 直接一刀切求解的真实痛点
很多人第一反应是:我有Gurobi、CPLEX,直接把整个MILP模型扔进去不就完了?小规模问题确实可以,设备种类少、典型日少、节点少的时候,求解器分支定界咔咔就做完了。但问题一旦往实际工程规模走,痛点就来了。
以我常见的园区级模型为例:候选设备几十台,典型日取4个(春夏秋冬各取典型日),每小时一个决策点,那就是96个时段。大M约束、储能时序约束、网络约束一铺开,变量数量轻松到几十万级,约束条件也差不多这个量级。直接求解时,求解器的分支定界树会膨胀到让人绝望,很多情况下几个小时跑完也拿不到一个像样的可行解,gap挂在两位数下不来。
这里要强调一个关键认知:MILP求解器内部本身也在做类似“分解”的事,但它是通用的,不知道你的问题结构。GBD的精髓恰恰是利用问题本身的“双重结构”——离散决策变量和连续运行变量天然分层——把一个大MILP拆成一个小MILP(主问题)和一系列规模更小的LP(子问题),让求解器在每个子问题上都轻松愉快。这就像做饭,与其一个锅里炖一大锅菜容易糊底,不如分开炒,每道菜火候都好控制。
1.3 GBD为什么在这里是好牌
广义Benders分解能在综合能源系统规划里站住脚,因为问题的结构跟它严丝合缝。主问题只包含离散决策变量(设备选型、容量档位)和一个人工变量,规模小得多;子问题在离散变量固定后变成纯LP或纯QP,求解极其迅速,并且能顺手给出对偶乘子——这正是生成Benders割的原料。还有一点经常被忽略:GBD天然容易并行化。各典型日的子问题彼此独立,完全可以同时求解,在多核机器上收益非常可观。
当然,GBD不是银弹,它也有打不过直接求解的时候,后面我会专门讲。但先记住一个结论:对于设备数量几十台以内、运行约束以线性为主的综合能源规划问题,GBD是一个结构清晰、代码可控、甚至比直接求解更快的选择。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 代码总体架构与核心模块划分
2.1 一个标准的GBD代码骨架
这套代码我按四个层来组织:数据层、模型层、求解层、结果层。数据层读入负荷曲线、设备参数、能源价格;模型层分别构建主问题(Master Problem, MP)和子问题(Subproblem, SP)的数学表达式;求解层控制迭代流程,维护上下界和割池;结果层汇总配置方案和运行方案,输出报表。
先看整体流程的伪代码,这比任何文字说明都直观:
python复制def benders_decomposition(
