1. 项目背景与优化问题定义
1.1 为什么把P2G和碳捕集设备放在同一个系统里研究
我做综合能源系统优化调度这块也有几年了,坦白说,早期看到P2G和碳捕集设备的组合模型时,第一反应是“这不就是把两个模块拼一起嘛”,但真正动手复现之后才发现,这两个设备放在一起会产生很强的耦合效应,远不是简单叠加。
先说P2G(Power to Gas,电转气)。它的原理分两步走,第一步是电解水制氢,2H2O电解生成2H2和O2;第二步是氢气和二氧化碳发生甲烷化反应,CO2加4H2生成CH4和2H2O。所以P2G设备本质上是一个“耗电耗碳、产出天然气”的环节。很多人做系统优化时把P2G简化成一个“电转气”的效率模型,忽略了对CO2的消耗,这在单独研究P2G消纳风电时问题不大,但一旦和碳捕集设备联合建模,就必须把这个CO2需求明确写出来,因为碳捕集捕下来的CO2正好可以作为P2G的原料。
碳捕集设备(Carbon Capture System,CCS,项目里具体做的是燃烧后捕集)则是另一个方向,它从CHP机组或锅炉的烟气中把CO2分离出来。主流做法是化学吸收法,用单乙醇胺(MEA)溶液吸收烟气中的CO2,再通过加热再生塔把CO2解析出来。这个过程需要消耗大量蒸汽,如果蒸汽来自CHP机组的抽汽,那就会影响机组的热电出力分配,这个耦合关系直接改变了系统的运行可行域。
两个设备合在一起之后,系统里就形成了一条很有意思的内部循环链路:CHP燃烧天然气发电产热,产生烟气,烟气进碳捕集装置,CO2被分离出来,一部分卖给外部或者封存,一部分送到P2G做甲烷化原料,P2G产出的天然气再回到天然气网络里供气负荷使用。用我的话说,这就等于在系统内部造了一条“碳的传送带”,而整个传送带的驱动力来自风电和电网买来的电。碳排放成本和经济运行成本的目标对冲,就藏在这条传送带的每一个环节里。
1.2 双目标优化为什么选epsilon约束法而不是加权求和
这个项目标题里写得很清楚,要同时优化碳排放成本和运维成本。这是一个典型的多目标优化问题,两个目标之间存在冲突:想多捕碳、多产气,就要多耗电多耗热,运维成本就上去了;想控制运行成本,可能就要牺牲一部分碳减排效益。
很多初学者拿到双目标问题,第一反应是加权求和,把两个目标乘上权重加起来变成一个单目标。我早年也这么干过,但用在实际复现里容易碰到三个麻烦。第一个是量纲问题,碳排放成本是吨乘以碳交易价格,运维成本是设备运维加购能费用,两个数量级可能相差好几倍,权重系数很难解释清楚。第二个是权重本身的主观性,拍脑袋定0.5和0.5,整个系统的最优运行点就变了,别人复现你的结果时没有办法还原你的决策逻辑。第三个也是更本质的问题,加权法在Pareto前沿是非凸的情况下,会漏掉一部分真实的Pareto最优解,画出来的前沿曲线形状是残缺的。
epsilon约束法完全是另一种思路。它把其中一个目标作为主目标,另一个目标转成不等式约束。拿这个项目来说,就是把碳排放成本当成主目标最小化,把运维成本写成约束条件,要求它小于等于某个给定的ε值。每给一个不同的ε,就得到一个不同的Pareto最优解,把ε从最小允许值一路扫到最大允许值,就能得到一条完整的Pareto前沿。
相比加权法,epsilon约束法有几个突出的优点。它不需要预先确定权重,避免了主观性;它每次求解的都是单目标优化问题,可以直接用成熟的求解器处理,比如Gurobi或者CPLEX;它生成的Pareto前沿点分布更均匀可控,可以通过设定等间距的ε序列来控制解的密度。这个方法对非凸问题同样有效,这也是为什么很多SCI一区文章在综合能源系统多目标调度上都采用它。
1.3 复现前需要理清的系统边界和调度尺度
在动手写代码之前,我强烈建议先把系统边界画清楚。这个项目考虑的是典型日前调度场景,时间尺度是24小时,步长1小时,也就是一共24个调度时段。系统内部的设备包括:CHP热电联产机组、燃气锅炉、P2G设备、碳捕集装置、风电机组,以及向上级电网和天然气网的购能接口。电负荷和热负荷是已知的预测值,风电出力是已知的预测序列,这些作为模型的输入参数传入。
这里有一个细节值得注意。标题里强调了“热电联供”,所以CHP在这个系统里承担的是供电压舱石和供热主力军的双重角色。它的电出力和热出力不是独立的,这个耦合特性会在模型里体现为热电联产可行域约束。为了方便建模,我在复现时把CHP按抽凝式机组处理,也就是说在一定的电出力范围内,热出力可以在一定区间内调节,而不是固定热电比。这种处理方式能更真实地反映机组运行灵活性,也是论文里比较常见的设定。
系统与外部网络的交互也需要注意。向上级电网购电是有上下限的,不能无限买;向天然气网购气也有流量上限。这些边界条件在优化模型里都是硬约束,如果漏掉任何一个,求解出来的“最优方案”可能在工程上根本不可行。我在最初复现时漏掉了电网交互功率的爬坡限制,结果算出来的24小时购电曲线在相邻时段出现了剧烈跳变,后来补上约束之后曲线才变得合理。
2. 综合能源系统建模与关键设备数学模型
2.1 电-热-气三种能量流的总体架构
整个系统的能量流动可以用三条主线来梳理。电流方面,风电出力、CHP电出力、上级电网购电共同满足电负荷、P2G设备耗电和碳捕集设备耗电;热流方面,CHP余热和燃气锅炉的出力满足热负荷和碳捕集装置再生塔的耗热需求;气流方面,天然气网购气和P2G产气共同供应气负荷,同时CHP和燃气锅炉消耗天然气。这三条能量流通过CHP、P2G和碳捕集装置实现耦合。
用公式化的语言来写系统的电功率平衡约束:
$$P_{wt}(t) + P_{chp}(t) + P_{grid}(t) = P_{load}(t) + P_{p2g}(t) + P_{ccs}(t)$$
热功率平衡约束:
$$H_{chp}(t) + H_{boiler}(t) = H_{load}(t) + H_{ccs}(t)$$
气功率平衡约束:
$$G_{buy}(t) + G_{p2g}(t) = G_{load}(t) + F_{chp}(t) + F_{boiler}(t)$$
三个公式看着简单,但每一项背后都有设备模型在支撑。尤其是P2G产气量和碳捕集捕碳量之间不是独立的,P2G产气需要消耗CO2,而CO2的来源主要就是碳捕集设备,这就在气平衡和碳排放平衡之间建立了一条隐含的耦合通道。我复现时的体会是,建模阶段最忌讳的就是把每个设备孤立地写成一个效率模型,然后简单叠加,那样算出来的结果看似合理,但实际上忽略了设备间的物质流约束,换一组参数可能就崩了。
2.2 CHP机组的运行可行域建模
CHP在这个系统里是最核心的设备,它的建模质量直接决定了整个优化结果的工程合理性。如果简单地写一个电出力上下限和热出力上下限,那就等于把热电联产机组当成了两个独立设备,完全没有体现“联产”的意义。
我采用的是抽凝式CHP的线性可行域模型。这类机组的特点是:在一定的电出力范围内,可以通过调整凝汽流量和抽汽流量来灵活调节热出力,但电出力和热出力之间存在一个耦合关系,这个关系通常表示为一个多边形可行域。简化的写法是一组线性不等式约束:
$$P_{chp}(t) - c_v \cdot H_{chp}(t) \geq P_{chp}^{min}$$
$$P_{chp}(t) - c_v \cdot H_{chp}(t) \leq P_{chp}^{max}$$
$$0 \leq H_{chp}(t) \leq H_{chp}^{max}$$
其中 cv 是电热特性系数。这组约束的物理意义是:在纯凝工况下机组的电出力有上下限,当从汽轮机抽汽供热时,凝汽发电能力下降,所以电出力和热出力之间存在一个反向的线性折减关系。虽然我在复现时对原论文的可行域做了一定简化(没有考虑背压工况段),但对日前调度这个尺度来说精度已经足够。
另外CHP机组的爬坡约束也不能忽略:
$$-R_{chp}^{down} \leq P_{chp}(t) - P_{chp}(t-1) \leq R_{chp}^{up}$$
如果不加爬坡约束,优化器可能会让机组从一个极端工况瞬间跳到另一个极端工况,这在数学上可行,但在物理上不可实现。我在最初复现时偷懒跳过爬坡约束,结果Pareto前沿上的某些解出现了很不合理的功率跳变,后来补上之后情况立刻就好了。
2.3 P2G设备的能量转化与CO2消耗关系
P2G设备的建模看起来简单,就是一个输入电功率、输出天然气功率的效率模型,但里面有几个坑值得单独说。
第一个坑是效率的层级。P2G的电转气全过程包含电解和甲烷化两个环节,每个环节都有各自的效率。在日前优化调度中,为了保持模型线性,通常把两个环节合并成一个总效率η_p2g,输入电能到输出天然气功率的关系写成:
$$G_{p2g}(t) = \eta_{p2g} \cdot P_{p2g}(t)$$
这个处理方式是论文里最常见的简化方式,它隐含假设了电解槽和甲烷化反应器在中低负载率下效率基本不变。实际操作中这个假设是可接受的,因为P2G在这个系统里主要承担的是消纳多余风电和利用捕集CO2的角色,不是作为主力供气源。
第二个坑是CO2消耗约束。P2G甲烷化反应需要CO2,根据化学反应计量关系,每生成1单位的CH4(按能量计),大约需要消耗固定比例的CO2质量。这个比例在模型中通常写成:
$$M_{co2, p2g}(t) = \beta_{p2g} \cdot G_{p2g}(t)$$
其中 βp2g 是CO2消耗系数。这笔CO2需求从哪里来?一部分来自碳捕集设备,不足的部分可能需要外购。如果系统里碳捕集的量不够P2G使用,就会产生额外的碳源成本,这个关系在做双目标优化的时候会自动体现出系统内部的权衡。我在代码里把CO2平衡写成了单独的约束块,并把它与碳排放成本目标函数关联起来,这样做的好处是,后续如果想研究“碳捕集容量配置”或“P2G规模扩展”这类问题,只需要改设备上限参数,不需要动主体模型框架。
第三个坑是P2G设备的运行上限和爬坡限制。P2G的电解槽虽然调节速度快,但也不能在1小时内从0跳到满负荷,我在复现时给它加了简化的爬坡约束。另外,P2G的启停会影响电解槽寿命,有论文做了启停惩罚项,但在这个项目里为了保持模型简洁,没有加入整数变量来精确建模启停状态,而是通过最小出力限制来近似避免频繁启停。如果你的场景对设备寿命损耗很敏感,可以考虑加入二进制变量,代价是求解时间会明显增加。
2.4 碳捕集设备的能耗与捕碳量模型
碳捕集设备的建模,核心要搞清楚三件事:捕了多少碳、消耗了多少热、消耗了多少电。
捕碳量取决于进入碳捕集装置的烟气量和捕集率。烟气量跟CHP机组和锅炉的燃料消耗量直接相关,燃料烧得越多,烟气CO2越多。简化模型里,系统产生的CO2总量可以近似写成:
$$E_{total}(t) = \alpha_{gas} \cdot (F_{chp}(t) + F_{boiler}(t)) + E_{grid}(t)$$
其中 αgas 是天然气燃烧的碳排放因子,Egrid(t) 是购电对应的间接碳排放。碳捕集装置捕集CO2的量是:
$$M_{capture}(t) = \eta_{ccs} \cdot k_{capture} \cdot E_{total}(t)$$
这里 kcapture 是碳捕集系统的CO2捕集率,通常取0.85到0.9。被捕获的CO2分两路走,一路作为P2G原料,一路可以外送或封存。在本文模型里,我优先让被捕集的CO2去满足P2G需求,富余量才算作系统的碳减排量。
碳捕集装置的能耗是模型里最容易被忽略、但影响又很大的部分。溶剂再生塔需要蒸汽,这个蒸汽在本文模型中折算为热功率消耗 Hccs(t),而吸收塔的风机、泵等辅助设备需要消耗电功率 Pccs(t)。这两部分能耗都跟处理的CO2量成正比,写成线性模型:
$$H_{ccs}(t) = \lambda_h \cdot M_{capture}(t)$$
$$P_{ccs}(t) = \lambda_e \cdot M_{capture}(t)$$
λh 和 λe 分别是单位捕碳量的热耗和电耗。这就是我之前说的耦合来源——碳捕集设备自己会“吃掉”一部分热和电,而这部分能量供给又分流了CHP和燃气锅炉的出力,对电负荷平衡和热负荷平衡都会产生压力。
我复现时曾经想过把捕集设备的能耗设成常数拉倒,结果发现捕碳量大时能耗占比相当可观,低负荷时又显得浪费。后来坚持用与捕碳量线性相关的模型,整个系统的运行结果合理多了。
2.5 储能设备和其他系统约束
原项目标题里没有提到储能设备,但我在复现时发现如果不加任何储能,系统的灵活调节能力会明显受限,尤其是当夜间风电出力大而电负荷小时,多出来的电只能通过P2G消化,P2G容量不够就得弃风。如果你参考的论文里带了电储能和热储能,那模型会更丰富,但要做的工作量也会大不少。
考虑到原项目是复现文章基础版配置(不含储能),我在主体代码里没有加入电储能和热储能,而是用P2G作为柔性负荷来提升风电消纳率。在讨论部分,如果后面想做扩展,把储能加进去是比较自然的方向。
其他系统约束还包括:电网交互功率上下限、气网购气量上下限、风电出力上限(即预测可用功率)、P2G和碳捕集设备的容量上限,以及各设备出力的非负约束。这些约束不是凑数的,每一个都对应着实际工程中的物理边界。写模型的时候,我建议把所有变量统一一个备用命名规则,比如P_开头的都是电功率,H_开头的都是热功率,G_开头的都是气流量,M_开头的都是CO2质量流,这样调试代码的时候能省下大量时间。
3. 双目标优化模型与epsilon约束算法实现
3.1 目标函数一:碳排放成本的定义与计算方式
碳排放成本是本项目的核心目标之一。它不是简单地把系统总碳排放量乘以碳价,而是要引入碳配额的概念,这是很多论文里都在用的机制。系统实际碳排放量超过配额的部分需要花钱购买碳排放权,低于配额的部分则可以出售获利。
碳配额的设定方式直接影响了碳排放成本的计算结果。常见的做法是按机组出力和发电量的比例分配免费配额,CHP机组发电越多,分到的配额越多,这是对供热为主的机组的一种政策倾斜。我在复现时采用的配额模型是:
$$E_{quota}(t) = \gamma_{elec} \cdot P_{chp}(t) + \gamma_{heat} \cdot H_{chp}(t)$$
其中 γelec 和 γheat 分别是单位电出力和单位热出力对应的碳配额系数。碳捕集系统捕集的CO2从总排放中扣除,所以系统实际需要购买配额的碳排放量为:
$$E_{net}(t) = E_{total}(t) - M_{capture}(t)$$
最终碳排放成本目标函数:
$$f_1 = \sum_{t=1}^{24} c_{co2} \cdot (E_{net}(t) - E_{quota}(t))$$
c_co2 是碳交易价格。这个模型算出来的碳排放成本可以为正也可以为负。如果碳捕集量足够大,E_net 小于配额,那系统实际上是卖碳配额获利,碳排放成本就是负数,这是完全合理的,代表低碳运行带来了经济收益。这一点在结果讨论时很值得讲,因为它直观展示了碳捕集设备的经济价值。
3.2 目标函数二:系统运行维护成本的定义
第二个目标函数是系统运行维护成本,在优化模型里它通常包含三块:向上级电网购电的费用、向天然气网购气的费用、各设备运行维护费用。
购电费用是分时电价结构,我采用峰谷平三段式电价,这在复现案例里比较常见。购气费用则按照天然气价格乘以购气量计算。设备运维费用采用单位出力的运维成本率乘以出力的形式,包括CHP、燃气锅炉、P2G和碳捕集设备各自的运维费用。综合起来:
$$f_2 = \sum_{t=1}^{24} [c_{buy}(t) \cdot P_{grid}(t) + c_{gas} \cdot G_{buy}(t) + \sum_{i \in \Omega} c_{om,i} \cdot P_i(t)]$$
其中 Ω 是所有设备的集合。注意我这里用P_i表示设备出力,对于产热设备在代码里要换成热量单位,否则量纲会出问题。写代码时我建议把目标函数二单独封装成一个函数,方便调试和对比不同参数下的成本结构。
有一点要提醒:碳排放成本里已经包含了碳交易费用,所以运维成本里不要再重复加任何碳相关费用,否则两个目标的物理含义就说不清了。这也是双目标建模时常见的边界混淆陷阱。
3.3 epsilon约束法的原理与完整伪代码
epsilon约束法的本质是将多目标问题转化为一系列带约束的单目标问题,具体到这个项目,就是保留碳排放成本作为主目标,把运维成本加上一个上限约束:
$$\min f_1(x)$$
$$\text{s.t.} \quad f_2(x) \leq \varepsilon_i$$
$$x \in \Omega_{feasible}$$
其中 Ω_feasible 是原问题的所有约束集合。每取一个不同的 εi 值,就求解一次上面的单目标问题,得到一组最优解和对应的两个目标函数值。把所有的 εi 遍历完,就得到了Pareto前沿的离散近似。
写代码的时候,ε序列怎么取是需要认真考虑的。通常的做法是,先单独最小化f1得到最优值f1_min,同时记录这个解对应的f2值,记为f2_max(因为在这个解下f2一般是最大的);再单独最小化f2得到f2_min,同时记录对应的f1值。然后就可以确定ε的取值范围是[f2_min, f2_max],把这一整个区间均匀分成N段,就得到了N+1个ε值。
我做的算法伪代码如下:
输入:系统参数、负荷数据、风电数据 输出:Pareto前沿点集合 Step 1: 求解约束化问题 求解 min f1,仅含原始约束,得到f1_min和对应的f2_max 求解 min f2,仅含原始约束,得到f2_min和对应的f1_max Step 2: 确定epsilon序列 设区间步长delta = (f2_max - f2_min) / N 生成序列 epsilon_i = f2_min + i * delta, i = 0, 1, ..., N-1 Step 3: for i = 0 to N-1: 求解 min f1 约束条件 = 原始约束 + f2 <= epsilon_i 记录解 x_i,计算f1_i和f2_i 将(f1_i, f2_i, x_i)加入Pareto集合 Step 4: 对Pareto集合做非支配排序,剔除被支配解 Step 5: 计算折中解(采用模糊隶属度法)这里有一个细节值得多说一句。第5步非支配排序不是可选项。由于整数变量的存在,某些ε值下求解出来的点可能不是严格最优的,或者出现两个不同ε对应同一个解的情况,导致前沿上出现冗余点。做一遍非支配排序可以把这些冗余点剔除,让Pareto前沿更干净。我在第一次写代码时跳过了这一步,画出来的前沿曲线有毛刺,加上之后平滑了很多。
3.4 折中解的选择方法与工程意义
Pareto前沿上的每个点都是数学意义上的最优解,但工程上,最终只能选一个调度方案执行。怎么选?这里需要引入一个“折中解”的概念,最常用的方法是模糊隶属度函数法。
对Pareto前沿上的每一个解k,计算它在第m个目标上的满意度:
$$\mu_{k,m} = \frac{f_{m}^{max} - f_{k,m}}{f_{m}^{max} - f_{m}^{min}}$$
然后把每个解对所有目标的满意度取平均:
$$\mu_k = \frac{1}{M} \sum_{m=1}^{M} \mu_{k,m}$$
满意度最大的解就是折中解,它的物理意义是两个目标的综合表现最均衡。在本文的双目标问题中,折中解对应的调度方案既不会太追求碳减排而让运维成本飙升,也不会太抠成本而让碳排放失控。
我用这个方法选出的折中解,两个目标值大概都处于各自极值区间的中间偏低碳一侧。这也是符合直觉的,因为碳交易价格给定的情况下,适当的碳捕集投入会带来碳配额的收益,相当于给系统增加了一个收入来源,所以折中解不会极端偏向单纯的经济最优。
4. Matlab代码实现与调试要点
4.1 开发环境与工具箱配置
整个项目我用的是Matlab R2023a版本,建模用YALMIP工具箱,求解器用Gurobi 10.0。YALMIP是Matlab环境下非常好用的优化建模语言,它的设计思路是让你用接近数学公式的方式写优化模型,然后自动调用后端求解器求解。
安装配置方面有几点经验分享。第一,Gurobi安装后需要获取license,学术用户可以直接申请免费license,个人用完全够。第二,Matlab里要用Gurobi,需要运行gurobi_setup脚本把路径加入Matlab环境中,然后在YALMIP里用solvesdp或者optimize时指定solver='gurobi'。第三,YALMIP版本不要太老,老版本对Gurobi新版本的支持有问题,我遇到过接口不兼容导致Gurobi一直报错的情况,升级YALMIP后就好了。
如果你的电脑没装Gurobi,用YALMIP自带的求解器比如sedumi或SDPT3也能算,但对大规模MILP问题求解速度会慢很多。这个项目是24时段的混合整数线性规划,变量规模不算特别大,用sedumi也许能跑,但我建议直接用Gurobi,效率高一个量级。
4.2 决策变量与主程序框架设计
我把整个程序分成四个模块:数据参数模块、模型定义模块、epsilon求解模块、结果分析模块。模块化的好处是,后续调整碳价、负荷曲线或者设备参数时,不需要动模型主体代码。
数据参数模块用结构体存储所有参数,包括系统参数、设备参数、电价气价、负荷曲线、风电预测数据等。我专门用一个init_parameters.m文件来写,方便对比不同场景时直接修改参数文件。
模型定义模块的核心代码如下:
% 决策变量定义(以24时段为例) P_chp = sdpvar(1, 24, 'full'); % CHP电出力,kW H_chp = sdpvar(1, 24, 'full'); % CHP热出力,kW P_boiler = sdpvar(1, 24, 'full'); % 燃气锅炉热出力,kW P_p2g = sdpvar(1, 24, 'full'); % P2G输入电功率,kW G_p2g = sdpvar(1, 24, 'full'); % P2G输出天然气功率,kW P_grid = sdpvar(1, 24, 'full'); % 电网购电功率,kW G_buy = sdpvar(1, 24, 'full'); % 气网购气流量,kW M_capture = sdpvar(1, 24, 'full'); % 碳捕集量,kg P_wt = sdpvar(1, 24, 'full'); % 风电出力,kW注意这里我用sdpvar而不是binvar,意味着这些变量默认是连续变量。CHP的启停状态在这个简化模型里没有显式建模,因为阶梯式最小出力可以通过可行域约束来近似。如果你参考的论文里考虑了启停,你需要额外引入binvar变量,模型的求解复杂度会上升不少。
4.3 约束条件的代码实现细节
约束条件在YALMIP里就是一堆等式和不等式,逐条写上去就行。但有几个细节要注意。
第一个是气平衡约束里的单位统一。天然气有体积(立方米)和能量(kWh)两种计量方式,模型里我用的是能量单位kW,气体负荷和P2G产气都用kW表示。如果你从外部数据源拿到的气负荷是立方米,需要乘以天然气热值换算(1立方米天然气约9.7kWh)再传入模型。
第二个是电功率平衡约束里,碳捕集设备的电耗不是独立变量,它是M_capture的函数,所以不需要单独定义变量,直接在约束里写成lambda_e乘以M_capture就行。同理,热平衡约束里碳捕集热耗也是M_capture的函数。
第三是P2G的CO2消耗约束,这里要连接M_capture和G_p2g。代码里我写的是一个等式约束:
M_co2_p2g = beta_p2g * G_p2g; % CO2消耗量 Constraints = [Constraints, M_co2_p2g <= M_capture]; % 捕集CO2优先给P2G直接写成等式的话,系统会强制所有P2G消耗的CO2都来自碳捕集,这在建模里有点太紧了,万一捕集量不够,P2G就不能满发,导致弃风加剧。我在复现时给P2G留了一条外购CO2的口子:
M_co2_buy = sdpvar(1, 24, 'full'); Constraints = [Constraints, M_co2_p2g == M_capture_to_p2g + M_co2_buy];然后在目标函数里对外购CO2加一个惩罚成本。这样做的好处是模型更贴合实际工程,缺点是目标函数里多了一项,碳排放成本的含义会稍微变化。如果只想忠实复现论文,不加外购CO2的口子问题也不大,因为大多数论文里都默认捕集CO2足够P2G使用。
4.4 epsilon循环求解的代码框架
epsilon循环是整个程序的核心,它把单目标优化问题包在循环里反复求解。我写的代码框架大致是这个样子:
% 第一步:求两个单目标极值 optimize(Constraints, f1, ops); f1_min = value(f1); f2_at_f1min = value(f2); optimize(Constraints, f2, ops); f2_min = value(f2); f1_at_f2min = value(f1); % 第二步:确定epsilon序列 N = 20; epsilon_seq = linspace(f2_min, f2_at_f1min, N); % 第三步:循环求解 pareto_f1 = []; pareto_f2 = []; for i = 1:N epsilon = epsilon_seq(i); Constraint_eps = [Constraints, f2 <= epsilon]; optimize(Constraint_eps, f1, ops); pareto_f1 = [pareto_f1, value(f1)]; pareto_f2 = [pareto_f2, value(f2)]; end有一个小坑要注意,linspace生成的epsilon序列,如果N取得很大,比如50以上,相邻两个epsilon对应的最优解差异可能很小,求解时间成倍增加,但Pareto前沿的改善很有限。我试过N=30和N=15,前沿曲线差别肉眼几乎看不出,所以一般取N=15到20就够用了。
另外,每次求解前不需要清理工作区变量,但建议在循环体内把上一次的optimize结果用assign函数重新赋值,避免YALMIP里面有些变量的初始值残留影响求解。我在写第一版代码时因为没注意这个问题,出现了偶发的不收敛,排查了很久才发现是上一次求解的初始点没有清理。
5. 算例分析与结果讨论
5.1 案例系统参数设置与数据说明
复现论文时,算例参数设置是最让人头疼的部分。原论文的详细参数往往藏在附录里,公众号或者复现包里一般只给一个大概的配置。我的做法是参考原论文里给出的系统结构图和数据表格,尽量还原一个典型冬季日的调度场景。
我这里提供一组基准测试参数,供没有原论文数据的读者参考:
| 参数项 | 数值 | 单位 |
|---|---|---|
| CHP容量 | 2000 | kW |
| CHP电效率 | 0.35 | - |
| CHP热电比范围 | 0.5~1.2 | - |
| 燃气锅炉容量 | 1500 | kW |
| P2G容量 | 800 | kW |
| P2G综合效率 | 0.60 | - |
| 碳捕集率 | 0.88 | - |
| 风电装机 | 1200 | kW |
| 碳交易价格 | 120 | 元/t |
| 天然气价格 | 3.2 | 元/m³ |
| 电网购电上限 | 1500 | kW |
| 碳排放因子(气) | 2.16 | t/MWh |
| 电网碳排放因子 | 0.58 | t/MWh |
电负荷和热负荷数据我采用的是典型冬季日的24小时曲线,峰值电负荷约2800kW,峰值热负荷约2200kW,风电出力夜间高白天低,有明显的反调峰特性。这些数据都放在init_parameters.m里,一行一个数组,方便替换。
5.2 Pareto前沿的形态与关键解读
跑完epsilon循环之后,把得到的(f1, f2)点画在二维坐标上,就是Pareto前沿。在我的基准算例下,前沿曲线呈现典型的下降趋势:碳排放成本从低到高变化时,运维成本从高到低变化,两个目标呈现明显的冲突关系。
曲线左侧(碳排放成本低、运维成本高)对应的是碳捕集设备全力运行、P2G尽可能多地消纳风电并制气的状态。这个时候CHP可能会降低出力,由电网购电和风电来平衡负荷,因为CHP燃烧天然气会产生大量CO2,增加碳排放成本。但同时,电网购电成本高,P2G运行也要耗电,所以运维成本上去了。
曲线右侧(碳排放成本高、运维成本低)则是相对保守的运行状态。碳捕集设备可能低负荷运行,P2G也不太开,系统主要靠CHP和燃气锅炉供能,从电网购电尽量少,这样设备运维和购能成本最低,但碳排放量最大,碳排放成本自然就高。
折中解落在前沿曲线的中间偏左位置。用模糊隶属度法选出来的折中解,碳排放成本比最小值大约高百分之二十左右,运维成本比最小值大约高百分之十左右,这个比例在不同碳价下会变化。碳价越高,折中解越偏向低碳侧,这说明碳交易机制对系统低碳转型有明确的引导作用。
5.3 不同碳价下的系统运行策略演变
我额外做了一组敏感性分析,把碳交易价格从60元/吨一直加到200元/吨,观察折中解对应的各设备出力变化。
碳价低的时候,系统倾向多开CHP,因为碳排放的惩罚成本低,CHP的效率优势能充分体现,P2G和碳捕集设备的利用率都不高。碳价升高之后,系统开始减少CHP出力,增加电网购电,同时碳捕集设备和P2G的利用率明显上升。碳价在150元/吨以上的时候,P2G基本处于满发状态,碳捕集设备也接近上限运行,系统几乎把所有能捕的碳都捕下来了。
这个结果从工程角度看很有参考价值。它说明碳捕集设备和P2G的投资是否能收回成本,很大程度上取决于碳交易价格水平。碳价太低的时候,这套设备就是纯成本项;碳价够高,它们才真正变成盈利单元。在做方案规划时,这些设备配置的边界条件就是碳价,而不是单纯的技术可行性。
5.4 风电消纳与碳捕集的联动效应
另一个值得关注的结果是风电消纳率。由于P2G的存在,夜间风电大发时,P2G会大量消纳风电来制气,这直接提升了系统整体的风电消纳率。在折中解下,风电消纳率可以达到95%以上,接近完全消纳。
更有意思的是,风电消纳和碳捕集之间存在一个双向促进的联动效应。P2G制气需要CO2,而碳捕集设备捕碳越多,P2G的原料就越充足。当碳价较高时,系统愿意投钱让碳捕集设备满负荷运行,捕下来的CO2一路送进P2G,P2G制气再替代一部分外部购气,形成了“高碳价→多捕碳→多产气→少购气→更低碳排放”的正向循环。这条循环链在结果图上看得很清楚,碳价从80元/吨升到160元/吨的过程中,购气量下降的幅度非常可观。
这个联动效应在单目标优化模型里也能体现,但双目标Pareto前沿把它呈现得更直观——前沿上的每一个点,实际上是这个循环链在不同“强度”下的运行结果。从低碳端到低成本端,对应的是这条循环链从“全力拉满”到“基本关停”的渐变过程。
6. 复现踩坑记录与后续扩展方向
6.1 建模阶段的高频错误
我在复现过程中踩了不少坑,有些问题折腾了大半天才找到原因。这里整理一个速查表,给后来者省点时间。
| 问题现象 | 根因 | 解决办法 |
|---|---|---|
| 求解结果出现负的P2G产气量 | 变量没有加非负约束 | 给所有出力变量加P>=0约束 |
| CHP热出力极端跳变 | 缺少热出力爬坡约束 | 在约束组中补充H_chp的爬坡限制 |
| Pareto前沿上出现大量重复点 | 没有做非支配排序 | 输出前增加去冗余步骤 |
| 目标函数值出现NaN | 单位不统一导致模型病态 | 统一kW、kg、元三种单位体系 |
| epsilon序列超出可行范围 | f2_max取的是f1最小值解对应的f2,而不是f2上界 | 先验证f2_max的可行性再生成序列 |
| Gurobi求解失败代码10005 | YALMIP版本与Gurobi接口不兼容 | 升级YALMIP到最新版 |
第一个问题其实是最常见的,新手写约束时容易忘记“出力非负”这种默认常识。YALMIP里如果变量没有加非负约束,求解器可能会让变量取负值来降低目标函数,结果就是负产气量,这在物理上完全说不通。
6.2 求解性能优化的实际经验
这个项目的MILP模型规模不算大,变量数量大约在两三百个量级,约束条件几百条,Gurobi求解单目标问题通常几秒到几十秒就能收敛。但epsilon循环要跑20次,加上两个极值点的求解,整体耗时大约几分钟到十几分钟。
如果想进一步提高求解效率,我有几个实践经验。第一,打开Gurobi的MIP gap容差限制,设到1%甚至2%,求解时间能缩短一半以上,对日前调度结果的影响很小。第二,给整数变量提供好的初始解,可以先用一次不加整数约束的松弛求解结果作为初始点,MIP的搜索会快很多。第三,如果机器内存不够或者求解时间太长,可以适当减少epsilon序列的个数,N从20减到12,前沿曲线形态变化很小,但时间能省将近一半。
6.3 从复现到扩展:几个值得尝试的方向
完成了基础版的复现,读者完全可以在这个模型基础上做扩展。我列几个我认为有价值的扩展方向。
第一个是加入储能设备。电储能可以平抑风电波动,热储能可以解耦CHP电出力和热出力,让CHP在碳价高时少发电、多产热,配合储能满足热负荷。加入储能后,风电消纳率和整体经济性都会有明显改善,这是一个值得做的方向。
第二个是把确定性优化换成鲁棒优化或随机优化。风电出力和负荷预测都是有误差的,把不确定性描述成场景集或模糊集,用分布鲁棒优化来处理,能让调度结果在真实运行中更可靠。这也是目前综合能源系统研究的前沿热点。
第三个是考虑碳捕集设备的动态特性。本文用的是线性静态模型,但实际碳捕集设备的溶剂循环、吸收塔负载变化都有动态过程,在更细的时间尺度(比如15分钟)下,动态建模会显著影响碳捕集量和能耗的精确度。
第四个是增加需求侧响应。电负荷和热负荷不完全是刚性的,引入可平移负荷、可削减负荷,让负荷侧参与系统调节,能在不大幅增加运行成本的前提下进一步降低碳排放。
6.4 关于论文复现心态的一点建议
最后说点题外话。复现一篇SCI论文的代码,很多时候最难的并不是优化算法本身,而是弄清楚论文里没有明说的那些前提条件。我复现这个Energy一区文章时,光是理解原系统的能量流框架和碳流框架就花了不少时间,参数调校又花了不少时间。这个过程确实煎熬,但一旦模型跑通,Pareto前沿画出来,你会有一种“终于拿到钥匙”的感觉。
我的建议是,不要指望一次性把整个系统全部跑通,先从最基本的版本开始,比如先用单目标优化只优化运维成本,把模型跑通,确认结果合理,再加入碳排放目标、再把epsilon循环加上。每一步都验证一下输出结果的合理性,这样出问题时你能快速定位是哪个环节的问题。我自己吃过这个亏,一上来就写双目标加epsilon循环,结果怎么调都出不来合理的Pareto前沿,后来回退到单目标排查,才发现是CHP可行域约束的符号写反了。
这个项目后续还可以做很多花样,但我觉得最核心的收获不是代码本身,而是建模思路:当你面对一个有多个设备、多股能量流、多种目标的系统时,怎么把它分解成可求解的数学问题,怎么判断哪些细节要精细建模、哪些地方可以简化处理,这种判断力才是做研究工作真正需要积累的东西。