这个复现项目我前前后后折腾了差不多三周,从拿到论文的思路梳理、数学建模,到Python代码一步步敲出来、跑通、和原论文结果对齐,中间踩了不少坑。现在把完整的理解和实操过程整理出来,给正在做可再生能源制氢、合成氨或者综合能源系统优化方向的朋友一个参考。这个项目到底做什么,一句话说清楚:在并网和离网两种模式下,针对风光互补电解水制氢再合成氨的整套系统,同时搞定两个优化问题——一个是设备容量怎么配(装机多大、储氢罐多大、电解槽多大),另一个是系统怎么调度(每小时电解槽吃多少电、储氢罐存多少放多少、合成氨装置怎么安排生产计划),全程用Python建模求解,重点是“复现论文结果”。这套东西的价值在于,它不是单纯的制氢,也不是单纯的合成氨,而是把“新能源发电-电解制氢-储氢-合成氨”整条链路放在同一个优化框架里算,并且同时对比并网和离网两种商业运行逻辑,做容量规划和运行优化的耦合分析。适合做新能源系统优化、氢能化工交叉方向的研究生和工程师参考。
1. 系统构成与两类优化问题的全景拆解
1.1 风光互补为什么比单一种类电源更值得做
先说系统层面。风电和光伏单独拿出来都有明显的短板:风电在冬季和夜间往往出力更大,光伏则是白天晴好天气才能满发,而且中午峰值明显。如果只装风电,电解槽在夏天无风时段可能只能趴窝;只装光伏,晚上和阴雨天就要停机。两种电源互补之后,出力曲线的波动幅度会明显收窄,这样电解槽的前端电源就能更稳定,储氢罐的压力波动也小一些,系统整体的设备利用率能拉高。这一点在工程上非常重要,因为电解槽最怕频繁启停和长时间低负荷运行,对寿命和能耗都不友好。
从复现论文的角度,风光互补也不是简单把两种电源加起来,而是要在优化模型里让容量配置决策做到“多少风电配多少光伏”这个比例也参与寻优。不同地区的风光资源特性不一样,最优配比会差很多。比如风资源夜强昼弱、光资源午强夕弱,两者天然错峰,配置得当可以显著减少弃风弃光,也能减少储氢罐容量需求。
1.2 制氢-合成氨链路各环节的定位
这个系统的核心链路是:风电和光伏发出的电,优先供给电解槽制氢;电解出来的氢气进入储氢罐缓冲;储氢罐出口接合成氨装置,氢气与氮气在高温高压和催化剂作用下反应生成氨。氮气来源一般是空分装置,通常假设可以稳定供应,不在优化变量里。整条链路的“瓶颈”往往不在发电端,而在电解和合成氨之间的缓冲——合成氨装置要求连续稳定的进料,而电解制氢的速率是跟随风光出力波动的,储氢罐就是平衡两者的关键缓冲环节。
还有一个容易被忽略的点:氨本身可以作为氢的载体,便于储运。但在这个项目里,合成氨被当作终端产品来建模,目标函数里计算的是氨的生产成本和收益,而不仅仅是把它当作储氢介质。这个定位决定了收益项的构建方式,后面建模的时候要特别注意。
1.3 并网模式与离网模式的本质区别
并网和离网是这个项目里最核心的对比维度。并网模式下,系统与外电网相连,缺电时可以从电网购电补足,多电时可以上网卖电。这意味着电网成了系统的“兜底池”,电解槽的供电可靠性大幅提升,甚至可以在电价低谷时段主动多买电制氢,在电价高峰时段少用电甚至卖电赚钱。但并网模式不是免费的,购电有成本,上网电价和购电价往往不对等,而且某些地区对大工业用户的容量电费、需量电费也有影响。
离网模式则完全不一样,系统必须依靠自身的风光出力和储能装置实现功率平衡,没有任何外部电源兜底。这就要求风电、光伏、储能(电储能和氢储能)的容量配置必须足够冗余,否则极端天气下就面临供氢中断的风险。优化模型里,离网模式的约束条件要比并网模式苛刻得多,求解难度和计算量也明显上升。
两种模式的下表对比,写代码做方案比选时有大用:
| 对比维度 | 并网模式 | 离网模式 |
|---|---|---|
| 功率平衡 | 与电网双向交互,可购可售 | 完全自平衡,无外部交互 |
| 容量配置 | 可适当降低储能冗余,依赖电网兜底 | 必须配置高冗余储能,保证连续供氢 |
| 电解槽运行 | 可在电价谷段增加功率,峰段降低功率 | 完全跟随风光出力,灵活性受限 |
| 目标函数 | 含购电成本与售电收益 | 不含购售电项,投资成本占比更高 |
| 求解难度 | 相对小,约束少 | 约束强,易出现不可行解 |
2. 容量优化建模:如何用数学语言描述“配置多少设备”
2.1 决策变量梳理
容量优化层的决策变量就是各个关键设备的安装容量。在我的复现里,变量集合包括:风电装机容量(kW)、光伏装机容量(kW)、电解槽额定功率(kW)、储氢罐容量(kg)、合成氨装置额定进氢速率(kg/h)。有些论文还会把电池储能容量也纳入变量,但这个项目里的主要缓冲是储氢罐,电池储能可以作为可选扩展,我复现时先把它固定为0,把问题聚焦在氢链路上。
这里有一个必须注意的点:电解槽的容量和风电光伏的装机不是一回事。风电光伏的发电功率是波动的,而电解槽只关注它的额定输入功率上限。一个合理的系统设计,风电光伏的峰值出力加起来往往大于电解槽额定功率,因为不可能全天候满发。但也不能大得离谱,否则大多数时候电解槽都吃不满,设备利用率和经济性都差。这就是容量优化的意义:在所有设备寿命周期内找到成本最小的容量组合。
为了后面单位统一,我建议把所有功率类变量统一用kW,能量类(储氢)统一用kg(氢气质量)。代码里要严格注意单位换算,我见过很多复现失败的项目,最后查出来是单位写错了,比如把kW和kWh混用,或者把kg和Nm³混淆。氢气密度大约是0.0899 kg/Nm³,在建模前最好先把单位换算关系写清楚,放在代码注释最前面。
2.2 目标函数:年化综合成本最小化
容量优化的目标函数是年化综合成本最小化。核心公式是:
总年化成本 = 设备投资年值 + 年运行维护成本 + 年购电成本 - 年售电收益(并网模式)
设备投资年值的计算要用资本回收系数(CRF)处理,不能简单把总投资除以使用年限。CRF的公式是:CRF = r(1+r)^n / ((1+r)^n - 1),其中r是折现率,n是设备寿命。比如电解槽寿命假设20年,折现率8%,CRF算出来约0.1019,意味着1万元的投资每年要摊入成本约1019元。风电光伏、电解槽、合成氨装置的寿命和运维费率都不一样,最好用表格列出再代入计算。
我用一个具体例子说明使用年限的影响:假设储能罐成本是5万元,寿命10年,折现率8%,CRF约0.1491,年值约7455元;如果寿命15年,CRF约0.1168,年值5840元。这个差异在容量优化的目标函数里会影响储氢罐的最优容量取值,所以复现时必须用和原论文一致的寿命参数。
2.3 约束条件:让容量配置方案在时序上可行
容量优化层的约束,最核心的是“时序可行性约束”。也就是说,配置出来的容量方案,放到全年逐时(或典型日逐时)的风光出力数据下,要能保证每一时刻的电功率平衡和氢气物料平衡都能成立。
电功率平衡约束可以写为:风电出力 + 光伏出力 + 电网购电功率(并网模式)= 电解槽耗电功率 + 其他固定负荷。这里的固定负荷包括合成氨装置的辅机、电解槽辅机、空分装置等,我复现时把这些简化成合成氨装置功耗的一个比例系数。
氢气平衡约束更关键:电解槽产氢速率 - 合成氨装置耗氢速率 = 储氢罐充放速率。而且储氢罐的储氢量有上下限约束,不能为负,也不能超过容量。合成氨装置的耗氢速率通常假设在一定区间内可调,但需要保证最小连续运行时间或者最小负荷率。这个约束是导致模型求解变难的主要原因,因为引入了时间耦合——上一时刻的储氢量会影响下一时刻。
在代码实现上,这类时序约束可以写成矩阵形式:如果时间步长是8760个小时,那么变量就有8760个电解功率、8760个储氢量、8760个氨产量。用线性规划求解时,约束矩阵的维度会到上万级别,但现代求解器处理起来仍然很快,关键是不要循环逐时刻写约束,而要用向量化方式批量生成约束矩阵,效率完全不一样。
# 关键:容量优化不是单独一个静态公式,而是把容量变量代入逐时运行模拟中, # 让所有时序约束都成立,这也是为什么容量优化和调度优化天然耦合在一起。3. 调度优化:在固定容量下,每一小时该怎么运行
3.1 调度优化与容量优化的耦合关系
容量优化回答“装多少”,调度优化回答“怎么开”。两者不是割裂的,容量优化的目标函数里本身就包含了对运行过程的模拟,而调度优化又是在容量给定的前提下进行的精细化运行寻优。在数学上,这是一个典型的两层优化问题(bilevel optimization),上层变量是容量,下层变量是逐时调度计划。
复现这个项目时,我采用的策略是“容量层用群智能算法(比如遗传算法或粒子群)寻优,调度层在每个候选容量方案上用线性规划精确求解”。这样做的理由是:上层的容量变量是离散连续的混合变量,而且目标函数的梯度信息难以解析表达,启发式算法更适合;而下层的调度问题是一个线性规划问题,决策变量在当前容量下是线性的,直接用线性规划求解器即可得到全局最优。这个框架的好处是能把非线性问题拆分,避免从头到尾用非线性求解器导致的不收敛或者解不稳定问题。
3.2 调度层的决策变量与目标
在给定容量方案后,调度层要决策的是系统全年逐时的运行计划。决策变量包括:电解槽输入功率每小时的值、储氢罐充放氢速率每小时的值、合成氨装置的生产负荷每小时的值、并网模式下与电网交互的购电和售电功率每小时的值。
调度层的目标函数是运行成本最小化,包括:购电成本(并网模式)、设备启停惩罚、弃风弃光惩罚(如果有)、以及可能的售电收益(负成本项)。这里有一个细节:如果容量优化的目标函数里已经包含了运行成本项,调度层就必须和容量层的目标函数保持协调一致,否则两层优化会“打架”。我的做法是,在一个统一的目标函数框架里,运行成本项就是总成本的一部分,调度层求解时只把投资成本当作固定常数剥离掉,这样目标函数的方向自然一致。
3.3 调度层的关键约束细节
调度层的约束要细化到设备运行特性。电解槽有几个典型的运行约束:最小负荷率约束(比如不能长时间低于30%负荷运行)、爬坡速率约束(功率调整速率有限)、启停次数约束。这些约束如果全部建模,会引入整数变量,使问题变成混合整数规划。复现时先跑通线性松弛版本,再加上整数约束对比结果差异,这是一个务实的循序渐进思路。
合成氨装置的约束相对温和:它的负荷率在一定范围内连续可调,但是调整速率不宜过快,否则催化剂床层温度波动大。在模型中通常体现为负荷爬坡约束。储氢罐的约束则包括容量上下限、充放速率上限,以及储氢量在调度周期末与周期初保持一致的周期性约束——如果没有这个循环约束,模型会把储氢罐在最后一个时段“掏空”,出现边界效应,结果就不真实了。
4. Python代码实现要点与完整流程
4.1 整体代码框架设计
整个复现项目我用的是面向过程与面向对象混合的写法。外层用一个配置字典管理所有经济参数和技术参数,中间层是数据预处理模块,核心层是容量优化和调度优化的求解模块,最后是结果统计与可视化模块。这种结构的好处是:换一组数据或者调一个参数,只需要修改配置字典,不需要动求解逻辑。
数据预处理是整个项目最耗时的一步。需要准备的数据包括:典型年的逐时风速序列、逐时太阳辐照度序列、环境温度、并网模式下的分时电价。风速转风电出力要用功率曲线,我用了一个简化的分段线性拟合:切入风速3 m/s、额定风速12 m/s、切出风速25 m/s,在切入和额定之间按三次方关系插值。光伏出力则使用辐照度乘以一个综合效率系数(一般取0.75~0.85,包含温度修正、逆变器效率、灰尘遮挡等损失)。
# 风电出力计算典型函数 def wind_power(v_wind, rated_power=1000, v_in=3, v_rated=12, v_out=25): """根据风速计算单台风机输出功率(kW)""" power = np.zeros_like(v_wind) mask_in = (v_wind >= v_in) & (v_wind < v_rated) mask_rated = (v_wind >= v_rated) & (v_wind < v_out) power[mask_in] = rated_power * ((v_wind[mask_in] - v_in) / (v_rated - v_in)) ** 3 power[mask_rated] = rated_power return power注意:这里用的是风速幂律关系模拟,真实风机的功率曲线会比这个复杂,但如果论文没有给出具体风机型号,这种简化方式完全足够复现容量优化层面的结论。
4.2 容量优化层的求解实现
容量优化我用的是遗传算法加线性规划的两层嵌套结构。外层遗传算法负责寻优容量组合,内层线性规划负责对每个容量组合计算最优调度。遗传算法的种群规模取30,迭代次数取50,编码方式为实数编码,三个基因位分别对应该地区风电光伏资源比、电解槽与新能源峰值比、储氢罐小时数。这里我没有直接编码容量绝对值,而是编码比例系数,因为比例系数能限制搜索空间在一个合理范围,收敛速度会快很多。
每一代中,对每个个体调用内层线性规划求解器,把求得的运行成本和按容量计算的等年值成本相加,作为该个体的适应度值(越小越好)。线性规划我用的是scipy.optimize.linprog,虽然它性能不如商用求解器,但对这个问题规模(8760时刻、约5类连续变量)完全够用,而且安装简单、跨平台好,学生党友好。如果追求更高的求解效率,可以考虑用开源求解器HiGHS或者GLPK,语法上几乎无缝切换。
4.3 调度优化的线性规划建模
调度优化核心是把电平衡、氢平衡、储能约束写成标准线性规划形式。目标函数向量是各变量的成本系数,等式约束矩阵表示电和氢的逐时平衡,不等式约束表示设备上下限。我把变量组织成一个大向量,依次排列:电解功率(8760)、储氢充放速率(8760)、合成氨耗氢速率(8760)、储氢量(8760)、并网交互功率(8760,并网模式才有)。
有一个很关键的代码实现细节:储氢量是累积量,所以约束矩阵中需要体现“储氢量等于初始量加上累计充放”的关系。这个关系可以写成严格的等式约束,但在构造约束矩阵时,8760个时间步会产生8760条约束、每个约束涉及多个变量,如果用循环写会非常慢。我用的是对角矩阵的差分形式:把“当前时刻储氢量 = 上一时刻储氢量 + 当前充放速率 × 步长”写成一维带状矩阵,然后一次性传入求解器。代码上可以用scipy.sparse构造,速度提升非常明显。
# 储氢量递推约束的稀疏矩阵近似结构伪代码 # 不逐小时循环,而是用稀疏矩阵批量构造 # 这里展示思路,实际使用时需根据变量排序调整系数位置 A_h2_balance = np.zeros((T, T*4)) for t in range(T): A_h2_balance[t, var_idx_h2[t]] = 1 A_h2_balance[t, var_idx_h2[t-1]] = -1 A_h2_balance[t, var_idx_flow[t]] = -dt4.4 结果可视化与敏感性分析
跑完优化后,结果可视化是复现工作很重要的一环。需要画的图通常包括:最优容量配置下的全年逐时功率平衡图(风电、光伏、电解槽、电网交互叠在一张图上)、储氢罐储氢量变化曲线、不同容量方案的成本对比散点图(也叫收敛曲线)。这些图不只是为了好看,是为了验证模型合理性——比如储氢量曲线如果出现频繁触顶触底,说明容量配置偏紧,系统运行的鲁棒性差;如果长期在半仓运行,说明容量冗余偏大。
我还额外做了几个单因素敏感性分析:购电价变化对最优容量比的影响、光伏组件成本下降10%对容量方案的影响、折现率从6%调到10%时总成本的变化。这些分析在原论文中通常作为扩展讨论,复现后自己加上去,对加深模型理解非常有帮助。而且投稿或者写报告的时候,敏感性分析几乎是审稿人必看的内容。
5. 完整复现流程与实操过程中的常见坑
5.1 从零复现的七步流程
第一步,精读原论文的数学模型,把目标函数每一项的物理意义和单位标注清楚。第二步,整理所有参数表,区分系统边界条件(如风速和辐照度序列)、设备技术参数(额定效率、寿命)和经济参数(单价、折现率)。第三步,单独建模风电和光伏出力,用已知的一年8760小时数据进行验证性测试——比如把装机设为1MW,看年发电量和资源数据是否匹配。第四步,搭建电解制氢-储氢-合成氨的物料平衡模型,先不考虑优化,给定固定功率手工推演几个小时的物理逻辑。第五步,把调度优化写成线性规划,固定一个参考容量方案,跑通求解器。第六步,实现容量优化外层搜索框架,与调度层耦合。第七步,对比论文结果并校核,输出图表。
我的经验是一步也不要跳。跳步的结果就是模型都写完了,但完全不知道是哪里算错了,最后只能从头排查。前面几步走扎实,每个模块单独调试过,最后合起来出问题的时候排查范围会小很多。
5.2 复现途中遇到的四个典型问题
第一个坑是数据粒度不一致。论文里风速是典型日级的平均值,但电价是半小时级的实时数据。直接混用会导致电解槽功率计算和购电成本计算完全失真。解决方法是先把所有时间序列统一重采样到同一个时间尺度(我用的1小时),并且对风速的典型日数据做了均值修正,否则风电量会被系统性稀释。
第二个坑是储氢罐容量变量的单位搞混。论文图表里储氢罐容量的单位是kg,但在目标函数里成本单位用的是万元/kg,而物料平衡里流量单位是kg/h。如果代码里初始化数据时没注意单位换算,目标函数值就会差到几千倍。这个问题的排查很费劲,因为模型表面上没有报错,但算出来的容量方案明显不合理。最后我是把目标函数每一项单独打印出来检查,才定位到单位问题。
第三个坑是线性规划模型不收敛或者无解。最典型的场景是配置的风光容量太小,在连续多天阴雨无风的情况下,氢平衡约束无法满足。模型报无解后,排查思路是先用“宽松惩罚”跑一遍找到接近可行的方案,再看是哪个约束项导致不可行。我通常先给储氢量加一个较高的虚拟惩罚成本,让求解器告诉我最缺的是充电量还是放电量。
第四个坑是遗传算法的随机性导致结果不稳定。同一组参数跑两次,最优容量的差异有15%。这是群智能算法的正常现象,解决办法不是换算法,而是设置固定随机种子、增加种群规模或者把编码的搜索空间缩小到合理区间。我最后把种群规模从20调到40,代数从50调到80,两轮最优解的偏差缩小到了3%以内,可以接受。
5.3 题目复现与改写的边界把握
需要注意的是,这个项目标题里明确写着“【复现】”,在校生和研究者复现论文时,一定要清楚区分“复现”和“抄袭”的界限。复现是指用自己的代码重新实现核心算法,并验证原论文的方法在公开数据或自选典型数据下的表现,这是学术研究的一部分。真正安全的做法是保留方法论的参考价值,但数据、参数甚至场景都换成自己的试验设置,最后在代码库中注明参考论文的出处。这种做法无论用于课程作业还是学术报告,都是业界默认的合理方式,也是复现类工作本身的意义所在。
一个重要的经验是,完全逐行对照别人的代码没有意义,很快就会发现自己的数据和参数和原论文对不上。我更倾向于只参考原论文的数学公式和算法思路,代码全部从零组织,这样跑出来的结果即使与原论文有偏差,也更容易解释偏差来源——参数不同、数据不同、假设简化程度不同。
5.4 实用调参建议与扩展方向
关于成本参数的取值,我给一个常见的参考范围:风电单位千瓦投资约6000~8000元、光伏约3500~4500元(这两年的组件价格走低,取低值更符合当前现状)、电解槽约5000~7000元/kW、储氢罐按公斤储氢容量计算约300~500元/kg、合成氨装置按每吨日产能约150~200万元的规模投资。这些参数直接决定了容量优化结果,如果不想手动整理,可以查近两年的行业报告按等年值换算。
扩展方向上,这个模型有四个很自然的延伸。一是把电池储能加入系统,研究“电-氢混合储能”的容量配比;二是加入碳交易收益项,在碳约束下重新算最优容量;三是把全年集中优化改成滚动优化(比如3天一个窗口滚动),处理真实运行中的不确定性;四是在调度层加入模型预测控制,应对风速和辐照度预测误差。这四条任何一个单拎出来都能做成一篇新的论文。
提示:如果发现线性规划求解速度无法满足全年8760小时的调度需求,可以考虑选取典型日(如四季各选1~3天)代表全年进行优化,然后通过全年仿真验证典型日方案的可行性。这是论文里常见的简化手段,也是快速验证模型逻辑的捷径。
6. 复现过程中的心得与扩展建议
最后说点我个人实际操作下来的体会。复现这类容量-调度耦合优化的项目,最大的收获不是跑通了一个程序,而是彻底理解了容量规划和运行调度之间的动态博弈关系。刚开始我总觉得把容量变量加进去、让算法自动寻优就万事大吉,但实际调试时才发现,容量方案稍微变化一点,调度结果就剧烈波动,目标函数曲面根本不是光滑的。理解了这个“两层互为因果”的关系,才算真正入门了综合能源系统的优化设计。
另外一个小技巧:复现过程中,建议把每一步中间结果都用CSV文件存下来,时间戳命名放在一个results文件夹里。比如“cap_500kw_wind_300kw_solar_opt_schedule.csv”。这样跑完遗传算法之后,可以回溯任何一个候选容量方案的调度细节,复盘时方便很多。虽然看起来多花了存储空间,但比每次重跑一遍算法省太多时间。
这个项目后续如果继续往深做,我建议把里面的确定性优化框架升级为考虑风光不确定性的鲁棒优化或者随机规划。换句话说,当前模型假设未来一年的风速和辐照度是已知的,这显然是理想化的;接入历史数据分布并做场景削减后,容量配置和调度策略对极端天气的适应能力会显著增强。这也是从“复现论文”走向“自己发论文”的最佳路径之一。