最近我在调一个融合电力、热力、天然气和碳流四种元素的综合能源系统优化调度模型,里面同时塞进了碳捕集电厂和需求响应两种灵活性资源。说句实话,刚开始我也觉得只是把两类成熟技术塞进一个模型里,真跑起来才发现,耦合关系、时间尺度、碳流平衡这些东西远比想象中复杂。这篇文章就盘一盘这个模型怎么做、为什么这么做、哪些地方最容易翻车。
文中所说的“综合能源系统”,不是论文里那种理想化的抽象系统,而是我实际在算的一个园区级多能互补项目:里面有天然气、光伏、电锅炉、燃气锅炉、热电联产机组、碳捕集装置,还有一堆可以转移或中断的电负荷和热负荷。目标很简单,就是在满足电、热、气需求的前提下,把一天的运行成本压到最低,同时把碳排放总量控制住。需求响应和碳捕集电厂之所以能“玩出花样”,核心原因是它们都能在时间维度上制造灵活性,只是前者管负荷侧,后者管发电侧。
这个模型真正难的地方不在建模本身,而在“电热气碳”四个系统之间的耦合关系。电负荷由燃气机组、光伏和电网共同承担,热负荷由热电联产和燃气锅炉供应,天然气则作为燃料输入参与调度,碳捕集装置又会反过来消耗电能和热能。一整套约束互相咬合,任何一个环节没处理好,求解器就直接给你一个不可行解。下面我把整个模型的思路、约束搭建、求解实现和实际踩坑过程完整复盘一遍。
1. 为什么把碳捕集电厂和需求响应放在一个模型里
1.1 综合能源系统的“电热气碳”四维耦合
先理清这个模型里的物理网络。典型园区级综合能源系统会有三个主要母线:电力母线、热力母线、天然气母线。电力母线上连着光伏、燃气轮机、外网购电、电储能和各类电负荷;热力母线上连着余热锅炉、燃气锅炉、电锅炉、蓄热罐和热负荷;天然气母线则连接气源、燃气轮机和燃气锅炉。这三条母线本身已经够复杂,碳捕集装置一进来,等于在电力母线和热力母线上又挂了一个“能耗大户”,同时还在系统里多引入了一条看不见的“碳流母线”。
很多人容易忽略的一点是,碳捕集电厂并不只是“发电的同时捕碳”这么简单。胺法碳捕集装置需要消耗大量蒸汽用于溶剂再生,这部分蒸汽如果从汽轮机中段抽取,就会导致发电出力下降;同时捕集过程还需要电耗来驱动压缩机、泵和风机。所以碳捕集电厂的净上网功率实际上是“毛发电功率减去捕集能耗”的结果。这就像你一个月工资两万,但房贷、车贷、日常开销加起来扣掉一万五,真正能自由支配的只有五千。建模时如果把毛功率当成净功率来优化,结果会乐观得一塌糊涂,实际运行根本达不到。
需求响应在这个模型里的角色则完全不同。它提供的不是能量,而是“时间上的可调度性”。比如工业用户可以把一部分可中断负荷调整到电价低谷时段,办公楼宇可以利用建筑热惯性提前蓄热。当碳捕集电厂因为捕集能耗增加而降低净出力时,需求响应可以在负荷侧做反向配合,把高峰负荷削减或转移出去,相当于给系统装了一个双向缓冲阀。
1.2 需求响应在模型里的真正定位
单纯的碳捕集电厂调度,本质上是一个发电侧优化问题:什么时候多捕碳、什么时候少捕碳、溶剂存储量怎么变。但实际运行中你会发现,碳捕集装置有一个很尴尬的特点:它需要连续运行才经济,频繁启停会造成溶剂损耗和能耗上升。因此它并不是想开就开、想关就关的,最优策略往往是维持一个相对稳定的捕集率,然后通过调整电、热负荷来配合它。
这时候需求响应就体现出价值了。模型里我用了两类需求响应资源:第一类是价格型需求响应,通过分时电价引导用户改变用电行为;第二类是激励型需求响应,通过签订合同约定可中断负荷和可转移负荷。在优化调度模型里,前者的体现方式是负荷弹性系数,后者的体现方式是转移量约束和中断量约束。两类资源叠加之后,系统可以在碳捕集电厂的捕集能耗高时,主动削峰;在光伏大发或电价低谷时,主动填谷。这样发电侧和负荷侧就形成了真正的双向协同。
我把这种组合方式看成“双杠杆”:碳捕集电厂是发电侧的灵活性杠杆,需求响应是负荷侧的灵活性杠杆,两者联动才能在电热气碳四个维度上实现整体最优。只用其中一个,模型的优化空间都会窄很多。
2. 调度模型整体框架与关键变量设计
2.1 多能流拓扑怎么搭
模型采用了典型的多时间尺度日调框架,时间分辨率取1小时,优化周期24小时。这样做的好处是既能捕捉到峰谷差异,又不会让变量规模失控。对于园区级系统,96个时段虽然更精细,但对数据要求太高,尤其是热负荷和建筑热惯性的参数往往达不到这种精度,强行用时只会增加模型病态风险。
拓扑结构上,我按能量母线把系统拆成了四个模块:电源模块、热源模块、气源模块、碳流模块。每个模块内部设备独立建模,模块之间通过功率平衡方程连接。电力平衡方程上,所有发电机出力加外购电加上光伏出力,减去电储能充电和碳捕集电耗,等于电负荷减去需求响应转移量。热力平衡类似,把所有热源的热出力相加,减去蓄热罐蓄热速率,等于热负荷。天然气平衡则要求气源供气量等于燃气轮机和燃气锅炉的耗气量加气负荷。碳流平衡则是这个模型里最特殊也最容易被忽略的一条约束:总碳排放量,减去碳捕集量,最终要小于碳配额或碳排上限。
这种模块化设计的最大好处是方便调试。跑出不可行解时,可以逐个模块单独测试,先让电力系统单独平衡,再让热力系统单独平衡,最后再加耦合约束,定位问题速度能快好几倍。
2.2 设备模型与决策变量清单
模型中的决策变量可以分为三类:连续变量、0-1整数变量和状态变量。连续变量包括各机组出力、各设备能耗、碳捕集量、需求响应转移量、储能充放电功率等;0-1变量包括机组启停状态、碳捕集装置运行状态、需求响应调用状态;状态变量包括储电、蓄热、溶剂存储等随时间演化的存量。
设备建模时有一个关键取舍:到底按静态效率建模还是按变工况建模。例如热电联产机组,如果按定热电比建模,模型会非常干净,但实际运行中热电比是随工况变化的。我这次采用了线性化的可行域约束,把机组的电出力和热出力限制在一个多边形可行域内,相比定热电比,它更真实;相比非线性曲线,它又能保持MILP求解的友好性。对于燃气锅炉,直接按热效率折算燃气消耗量;对于电锅炉,按电热转换效率将耗电量映射为热出力,这两个设备都很简单,真正的复杂度都在碳捕集和需求响应上。
碳捕集电厂的内部变量尤其值得注意。捕集装置有一个“烟气分流比”的概念,你可以把一部分烟气直接排放,另一部分送入吸收塔。这个分流比可以连续调节,意味着捕集率不是固定的,而是由调度决策内生决定的。再加上溶剂存储环节,相当于给碳捕集系统增加了一个“能量仓”,高峰时段可以降低捕集率释放电能,低谷时段提高捕集率多捕碳,这就是碳捕集电厂能参与调峰的根本原因。
2.3 目标函数怎么设计才不跑偏
目标函数我最终采用了系统总运行成本最小化。具体包含四项:燃料成本、从外部电网购电成本、需求响应补偿成本和碳排放成本,外加机组启停成本。燃料成本按天然气消耗量乘以气价计算;购电成本按分时电价计算;需求响应补偿成本按可中断负荷容量费加电量费用计算;碳排放成本直接影响决策,比如碳配额内免费,超额部分需要购买碳配额。
设计目标函数时最需要想清楚的一点是:碳价到底放进去还是当成约束?我两种方案都试过。如果只作为约束,系统会优先用成本最低的方式把所有碳排放压到配额线以下,不会产生“多减排换收益”的激励;如果把碳价放进目标函数,那么系统会根据碳价和捕集能耗成本自动权衡减排力度。我最终选择了后者,碳价给到一个合理区间,这样碳捕集装置的投运与否、捕集率高低都由经济性驱动,而不是人工强行指定。
还有一个小陷阱:目标函数不能只看总成本,还必须给储能或蓄热设备保留合理的运行空间。如果分时电价变化不大,储能设备很可能在优化结果中完全不动作,这倒不是模型错误,而是经济上确实不划算。这个结果本身是有价值的,但如果你希望看到储能参与调峰,就得检查分时电价差是否足够覆盖储能损耗成本。
3. 约束条件构建:最容易翻车的地方
3.1 碳捕集电厂的运行约束与能耗惩罚
碳捕集电厂的建模是整个模型里最容易出错的地方。先说净出力约束。假设机组毛出力为 P_gross,捕集能耗为 P_cc,那么并网净出力是:
P_net = P_gross - P_cc
这个公式看起来简单,但P_cc本身不是常数,它取决于烟气流量和捕集量。工程上常用简化方式是按捕集量折算电耗和热耗,也就是每捕集一吨CO2需要消耗固定数量的电能和蒸汽。例如设定每吨CO2捕集电耗为100千瓦时,热耗为2吉焦,那么捕集量越大,净出力压降越明显。
烟气分流比也是一个需要考虑的变量。设总烟气含碳量为 E_total,分流进入吸收塔的比例为 split,则捕集量为:
C_capture = split * E_total * capture_efficiency
未捕集的烟气和吸收塔尾气共同构成系统净排放。这条约束天然带有非线性,因为 split 乘 E_total,但E_total又取决于机组出力,处理方式是引入一个大M变量或直接在0-1状态变量下分段线性化。
我的实际做法是:0-1变量表示“捕集装置是否投入运行”,投入时烟气分流比只能在上下限之间变化,不投入时捕集量和能耗全部为0。同时给碳捕集装置加一个最小技术出力约束,防止模型为了省钱把捕集率设成一个极低但又不切实际的数值。这个看似很小的约束,在实际求解中对可行域影响非常大。
3.2 需求响应负荷的柔性边界
需求响应建模要特别注意不要“过度柔性化”。真实用户可以调整的负荷永远是有限的,不可能一天之内把所有负荷凭空消失或凭空增加。我在模型里用到三个核心约束:
可转移负荷的平衡约束:所有时段转入的总负荷量必须等于转出的总负荷量。例如一天内从高峰时段转移出去100兆瓦时,就必须在低谷时段补回来100兆瓦时,否则相当于凭空省了电,结果一定失真。
转移时段限制:单个负荷的转移时长必须小于合同约定的最大转移时间。比如约定一次最多转移3小时,那就不能用5小时的长时转移来实现削峰。
可中断负荷容量约束:每个时段最多中断的负荷量不能超过签约容量,而且中断后必须支付对应的补偿费用。中断负荷不像转移负荷那样需要“还回来”,它本质上是用钱买服务,所以约束较为简单,但成本项要算清楚。
需求响应还有个容易忽略的副作用:转移负荷会改变系统原始负荷曲线,进而影响碳捕集电厂的最优运行策略。这两者之间是强耦合关系,所以不能先定负荷曲线再优化碳捕集策略,必须在同一个模型里同时决策。
3.3 热网/气网耦合约束处理
电、气、热三个系统的耦合,主要体现在热电联产机组上。它同时吃天然气,发电力,产热力,所以它的运行点必须在“电出力-热出力-耗气量”三维空间中可行。如果用定热电比,约束很简单,但会牺牲灵活性;我更推荐用可行域法,把机组启停、爬坡、最小出力、最大出力都写成线性约束。
热力系统还有一个建筑热惯性的问题。园区供冷供暖负荷不是刚性不变的,建筑物本身可以蓄热,提前一小时供暖也不会让室内温度立刻超标。模型里我用了一个简化的一阶等效热容模型,把热负荷变成可延迟的柔性负荷,并设置室内温度上下限约束。这个模型的引入让热负荷时序直接受到电负荷和碳捕集装置运行状态的影响,协同效应一下子就出来了。
天然气网络在园区级别通常可以简化处理,不考虑管网的动态特性,只按小时气量平衡约束限制购气上限。但如果要考虑管网压力约束或者气源供气波动,就必须引入Weymouth方程,它是典型的非线性约束。如果求解器处理不了,可以做成分段线性化,或者干脆用迭代法先算压力场再更新气量可行域,避免直接非线性求解带来的不收敛问题。
3.4 碳流平衡与碳排放约束
碳流平衡约束是这个模型和其他普通经济调度模型最大的区别。系统总碳排放包含两部分:燃气轮机的燃烧排放和燃气锅炉的燃烧排放;如果外购电也会产生间接排放,还需要按电网排放因子折算。总排放量减去碳捕集量,等于最终净碳排放量,这个净排放量要小于系统碳配额或碳排放上限。
还有一个容易被忽略的约束是溶剂存储平衡。捕集下来的CO2如果马上送去封存或利用,受限于下游消纳能力;如果你在模型里考虑了溶剂罐库存,就相当于给碳捕集系统增加了一个存储通道,可以在低电价时段多捕碳储存在溶剂中,在高电价时段减少捕集输出更多电力。这其实是全模型里最出彩的一个灵活调节手段,它的价值相当于给发电侧也装了一块“碳储能电池”。
溶剂罐约束形式为:当前时段库存等于上一时段库存,加上本时段捕集量,减去本时段解吸或外运量。库存有上下限,末端库存一般约束为不小于初始库存,以保证日调度的可持续性。不加这条约束,模型极有可能在最后一个时段疯狂捕碳,把一整天没捕的碳全部“补作业”,结果完全失真。
4. 求解思路与代码实现要点
4.1 模型线性化与求解器选择
模型规模大致是这样的:24个时段,4台主要设备,3个储能装置,约2000个变量,其中整数变量不到100个。这个规模对标准MILP求解器来说非常轻松,用开源求解器也能算,但我实际用的是商业求解器,差别主要在求解速度和对数值稳定性的容忍度上。如果你的模型再往上扩展到几百个节点或8760小时,建议直接上商业求解器,并开启求解器的MIP gap容忍度设置,别死磕最优性。
为了让求解器吃得消,所有非线性都做了线性化处理。煤耗曲线采用分段线性逼近;机组可行域用多面体约束表示;烟气分流比乘机组出力这种双线性项,通过McCormick包络或大M法线性化;天然气管网压力约束直接忽略或分段线性化。整个过程看起来繁琐,但本质就是一句话:能用线性表达式写出来的,绝对不用非线性函数。
4.2 核心建模骨架参考
我用的是Python加MILP建模语言搭的模型,下面给出几个核心代码片段,仅供参考。目标函数部分的逻辑是先定义设备运行成本、购电成本、需求响应补偿和碳排放成本,最后汇总取最小化。约束方面,最核心的是电力平衡约束和碳捕集能耗约束,这两个写对了,模型基本就立住了一半。
目标函数代码段示例:
model.obj = pyo.Objective( expr=sum( fuel_cost[t] + buy_power_cost[t] + dr_cost[t] + carbon_cost[t] for t in model.T ), sense=pyo.minimize )电力平衡约束示例:
def power_balance_rule(model, t): return ( model.P_chp[t] + model.P_pv[t] + model.P_buy[t] + model.P_discharge[t] - model.P_charge[t] - model.P_cc[t] - model.P_eb[t] == model.P_load_base[t] - model.P_dr_shift_out[t] + model.P_dr_shift_in[t] )碳捕集能耗约束示例:
def cc_energy_rule(model, t): # 捕集电耗正比于捕碳量 return model.P_cc[t] == model.cc_electricity_intensity * model.C_capture[t]多时段多设备耦合比较大的项目,建议每个约束函数只做一件事,命名尽量直白。因为后面调试不可行解时,你要能在控制台里快速定位是哪条约束在哪个时段卡住了,函数名写得太抽象会非常折磨人。
4.3 参数设置与典型结果解读
参数设置这一块我直接给一个可以照抄的基准值:天然气热值取35.6兆焦耳每立方米,气价按2.8元每立方米;光伏装机10兆瓦,预测曲线采用典型的晴天曲线;分时电价峰平谷分别为1.2、0.8、0.4元每千瓦时;碳配额设为一周平均排放量的90%左右,碳价按80元每吨。碳捕集装置的单吨电耗设为110千瓦时每吨,热耗设为2.5吉焦每吨。
在这个基准参数下,我跑了四组对照:第一组什么都不加,第二组只加碳捕集,第三组只加需求响应,第四组两个都加。结果很有意思,只加碳捕集时,系统总成本上升了约12%,因为捕集能耗本身是净负荷,会推高购电和燃料成本;只加需求响应时,系统成本下降了5%,主要来自峰时购电量的削减;两个都加时,总成本只上升了4%,比单独加碳捕集的12%明显低。原因是碳捕集装置在低谷时段多捕碳、在高峰时段降低捕集率让路给电负荷,需求响应又把高峰负荷往低谷转移,等于给碳捕集制造了更多低压运行空间。两者叠加产生的系统协同效应,就是这类模型最有价值的产出。
5. 实操中的坑与排查技巧
5.1 碳捕集能耗参数设错导致无解
我第一次跑这个模型时,直接给碳捕集装置设了固定电耗常数,没把它和捕集量挂钩。结果在光伏出力最高的中午时段,模型让碳捕集装置满负荷捕碳,净出力骤降,电力平衡却要求它对外输出大量电力,最后无解。排查了很久才发现问题出在能耗方程上。后来把捕集电耗改成与捕集量线性相关,并在捕集装置投入运行的状态下加上最小捕集率约束,模型才稳定下来。
这类问题的典型特征是:无解的时段集中在某个极端工况附近,比如光伏大发、负荷低谷或负荷尖峰。排查时不要一个时段一个时段瞎试,先把平衡方程拿出来,手动核算一下该时段“总供给减总需求”是否真的闭合,多数情况下马上就能看到是哪一方被约束卡死。
5.2 需求响应削峰填谷不明显的常见原因
调参时曾遇到一个问题:需求响应成本调得很低,模型却几乎不调用需求响应资源。后来检查发现,需求响应的补偿成本虽然低,但转移负荷增加的时段恰好也是电价较高且机组已接近最大出力的时段,模型为了让转移过来的负荷落地,必须加开机组或增加购电,这部分额外成本抵消了需求响应的收益。换句话说,需求响应不是“负荷软了就行”,还要看负荷转移目标时段的发电侧是否存在接纳空间。
解决办法是给需求响应加一个最小调用时长约束,并检查低谷时段是否有足够的备用容量。如果低价时段机组已经压到最低出力,那需求响应转移过去的负荷反而会推高成本,模型自然不愿意调用。这是很反直觉的一点:需求响应削峰填谷的前提是谷时段真的“有谷可填”。
5.3 热负荷时序与电负荷耦合导致模型病态
还有一次,模型出现了“抖动解”,相邻时段的蓄热罐充放速率来回剧烈变化,成本却没有明显改善。原因是热负荷等效热容模型时间常数设得太小,导致模型认为建筑热惯性几乎不存在,蓄热罐成为唯一的热缓冲手段。把热容时间常数从0.5小时调整为2小时后,抖动现象马上消失。
这提醒我一个原则:模型柔性越大,越要关注解的合理性。柔性负荷也好,储能也好,加多了以后,模型会出现大量“不花钱”的调节空间,求解器会在这些空间里来回切换最优解,导致对结果分析失去意义。所以每个柔性资源都要配上现实中的响应时间常数、爬坡限制、最小运行时间,不能为了“好看”而无限制地增加自由度。
5.4 快速校验模型的一些土办法
调试模型时我有几个土办法,虽然不高级,但非常管用。第一,把所有柔性资源参数设成极端值,比如需求响应补偿设为0、碳价设为0,看模型结果是否退化成一个普通的经济调度问题,如果结果和预期逻辑不一致,说明某些约束写错了。第二,把全天负荷和可再生能源出力乘一个系数,比如整体缩小10倍,看模型结果是否也等比例变化,如果出现非线性放大,多半是某条约束漏了单位换算。第三,随时用“检查每个时段的电力供给减需求是否为0”的脚本做后处理,即使求解器报告最优,也不要完全相信,因为数值容差偶尔会让个别时段的平衡偏差超限,虽然大问题不至于,但会严重影响碳排核算精度。
这套模型我前后迭代了将近三周,从最初的根本跑不动,到后来能稳定输出可解释的调度方案,中间踩的坑基本都集中在耦合约束的线性化、柔性资源的参数标定和数值稳定性上。综合能源系统优化调度,理论上很美,落地时全是细节。如果你也在折腾类似模型,建议别急着把模型搞复杂,先把电、热、气、碳四条主线的平衡方程写对,再逐步叠加需求响应和碳捕集的灵活性,一步一步来,模型才能真正跑出让你信服的结果。