这几年EI论文里梯级水光互补的短期优化调度模型出现得很频繁我完整复现过一篇以“最大化可消纳电量期望”为目标函数的模型用Python写了完整代码踩了不少坑也把求解流程顺下来了。这篇文章把模型思路、数学约束、代码架构和调试经验一次性讲清楚适合正在复现EI论文、做水电光伏联合调度方向的研究生也适合想从确定性调度转向随机调度的工程师参考。1. 模型核心思路与问题定义1.1 梯级水光互补到底补的是什么梯级水光互补系统不是简单地把水电站和光伏电站放在同一个算例里而是利用梯级水库群的空间连接关系和调节能力去平抑光伏出力的波动性。光伏出力白天高、夜间低天气突变时出力可能短时间内剧烈波动而水电机组响应速度快可以快速调整出力。当光伏大发时让水电少发甚至停机把水存起来光伏出力掉下去或者晚高峰负荷上来时再让水电把存的水放出来发电。这是“互补”的第一层含义。第二层含义在“梯级”上。梯级电站之间不是孤立的上游电站的发电流量和弃水流量会直接进入下游水库形成水量上的接力关系。所以优化调度不能只看单个电站必须把整条河段上的水库当成一个耦合系统来看。上游今天怎么调度直接影响下游明天有多少水可用这种时间上的转移和空间上的连接让模型规模变大也让求解难度上升。短期优化调度的典型时间尺度是日前24小时调度时段可以取1小时也有论文取15分钟一个时段也就是96个点。时间粒度越细模型越贴近实际但变量数和约束数成倍增加。我在复现时先用1小时粒度完成调试确认逻辑正确后再切换成15分钟粒度这样能省去大量排查问题的时间。1.2 “可消纳电量期望”的工程含义可消纳电量这个词直白说就是电网真正能接受、不会被迫弃掉的电量。水电和光伏发出来如果受外送通道限制、负荷需求限制或系统备用限制而无法上网那部分发电量就成了弃水弃光电量对调度人员来说这是浪费。所以这里的优化目标不是简单“发电量最大”而是“可消纳电量最大”也就是希望水电和光伏的总并网电量尽可能贴近系统允许的消纳上限。期望这个词说明模型考虑到了不确定性。光伏出力预测有误差天然来水预报也不可能完全准确如果在调度时刻只用一个确定性预测值一旦实际发生偏差计划就可能不满足实际约束。随机规划的做法是把可能出现的光伏场景和来水场景都离散出来每个场景赋予一个概率目标函数对所有场景的可消纳电量做概率加权平均得到期望值。这样得到的调度计划对整体不确定性来说是“平均意义上最好”的而不是只针对单一预测值最优。用生活里的例子类比就好比你计划出行时不能只按“路上一定不堵车”来规划而是要考虑几种可能的拥堵情况选一条在所有情况下总用时期望最小的路线。这个模型做的就是这个事情只不过把“路况”换成了光伏和来水场景。1.3 模型假设与边界条件复现这类EI论文第一步不是写代码而是把模型的假设条件搞清楚。我复现的这篇模型有几个关键假设短期调度周期为一天弃水、弃光行为允许发生未消纳的电量不计入收益水库水位库容关系采用简化曲线忽略水头随库容变化的非线性细节或者用分段线性近似梯级电站之间的水流时滞忽略不计或者按固定时段滞后处理光伏出力场景和来水场景已经提前生成模型只做日前决策不涉及实时修正电网侧用一个随时间变化的消纳能力上限来表示不建立完整潮流模型。这些假设是短期随机优化调度论文里的常规设定好处是模型可控、可复现性强。缺点是如果你直接拿去做工程应用需要补充网损、水头非线性、机组组合等细节。但作为EI论文复现这套假设足够支撑完整研究链条。2. 数学模型目标函数、约束与不确定性处理2.1 目标函数可消纳电量期望怎么表达目标函数写成最大化所有场景下可消纳电量的期望标准形式是max E[ ∑_{t∈T} ( P_{h,t}(ξ) P_{pvuse,t}(ξ) ) · Δt ]其中 P_{h,t} 是水电在时段 t 的并网功率P_{pvuse,t} 是光伏实际被消纳的功率Δt 是一个时段的时间长度ξ 表示随机场景。写成场景加权形式就是max ∑_{s∈S} π_s ∑_{t∈T} ( P_{h,s,t} P_{pvuse,s,t} ) · Δt这里的 π_s 是场景 s 的概率S 是场景集合。目标里没有发电成本项因为模型只关心电量消纳最大化这在以消纳为核心目标的调度论文中非常多见。我刚开始复现时犯过一个理解上的偏差以为“可消纳电量”等于“系统负荷需求”于是把等式约束写成水电加光伏必须等于负荷。后来仔细看论文才明白消纳能力往往是一个上限约束也就是说水电和光伏的总出力不能超过消纳空间但可以低于它。如果强行写成等式模型就会出现大量弃电甚至在某些场景下无解。目标函数和约束之间要配合好这是整个模型成立的第一步。2.2 水力约束水量平衡和电站出力水库的核心约束是水量平衡方程对每个电站 i、场景 s、时段 tV_{i,s,t1} V_{i,s,t} ( Q_{in,i,s,t} Q_{release,i-1,s,t} - Q_{turb,i,s,t} - Q_{spill,i,s,t} ) · Δt其中 V 是库容Q_in 是天然入流Q_release 是上游电站出库流量包括发电流量和弃水流量Q_turb 是发电流量Q_spill 是弃水流量。这里最容易出错的是单位换算库容常用万立方米或亿立方米流量常用立方米每秒两个单位乘时间时必须统一。如果 Δt 用小时需要把流量乘以 3600 再乘以小时数才能变成体积量这一步错整个模型基本不可行。除水量平衡外还有库容上下限、出库流量上限、发电流量上限和水电出力上限。水电出力 P_h 在短期调度里常用线性或分段线性函数近似P_h K · Q_turbK 是综合出力系数与水头、机组效率相关。如果论文考虑水头变化会让这一项变成非线性求解难度立即上一个台阶。EI复现里很多模型会做分段线性化或者直接用常数 K。我建议先按常数 K 复现再逐步加入分段线性水头影响这样便于定位计算问题。2.3 光伏消纳与系统消纳上限约束光伏部分有两类约束。第一类是光伏实际消纳功率不能超过该场景下光伏预测出力0 ≤ P_{pvuse,s,t} ≤ P_{pv,s,t}如果 P_{pvuse} 小于 P_{pv}就意味着弃光了这是模型允许发生的。第二类是系统整体消纳约束P_{h,s,t} P_{pvuse,s,t} ≤ L_tL_t 是时段 t 的消纳能力上限可以理解为外送通道容量或系统可接纳的新能源空间。这个约束决定了弃水弃光的上限也是“可消纳电量”的物理边界。我在复现阶段会把 L_t 单独做成一个数组而不是直接写成一个常数。不同地区不同时段的消纳能力差异很大比如白天光伏高峰时消纳压力大午夜负荷低谷时也难消纳只有把 L_t 做成时变数据模型结果才真正有意义。2.4 不确定性的场景化建模场景化建模是随机规划的关键。光伏场景通常用预测出力加误差扰动生成P_{pv,s,t} P_{pvforecast,t} ε_{s,t}误差项 ε_{s,t} 可以服从正态分布也可以从历史预测残差中抽样。来水场景类似可以用一阶自回归模型或者历史典型年份来水构造。生成场景以后还要做场景削减否则场景数量过大会导致模型变量爆炸。常用办法是同步回代削减法把大量初始场景聚合成几十个代表性场景同时重新分配概率。Python 里可以用 scipy 做基础抽样场景削减可以用场景树工具或者自己写回代算法。如果不想自己写最简单的方式是先抽 500 个场景再用 K-means 聚成 50 个簇把簇内的样本概率加总作为场景概率。这个方法工程上非常稳定复现已经足够。3. Python实现程序框架、建模语言和核心代码3.1 程序整体框架我的代码结构分成四层数据层读入水库参数、光伏预测、来水预测、消纳能力曲线场景层生成随机场景并做削减输出每个场景的光伏出力和来水过程模型层用 Pyomo 构建目标函数和约束调用求解器分析层读取求解结果绘制出力曲线、库容曲线和弃电率统计。这样分层的好处非常明显。调试时只需要替换某一层的数据或逻辑不用把整个代码翻一遍。比如从 1 小时粒度改成 15 分钟粒度只需要调整时段索引和 Δt模型层完全不用动。3.2 建模语言与求解器选型Python 生态里做优化建模我首选 Pyomo。它支持线性规划、混合整数规划、非线性规划后端可以对接开源求解器 CBC、GLPK也可以对接商业求解器 Gurobi、CPLEX。对于这种大规模线性随机规划模型Gurobi 的求解速度和稳定性明显优于开源求解器学校环境通常能申请到学术 License。如果只能用开源方案CBC 也能求解但场景数超过 200 后速度下降得很明显需要配合场景削减使用。如果你不想安装 Pyomo也可以用 scipy.optimize.linprog但需要手动把约束矩阵展开代码可读性和扩展性都会变差。复现 EI 论文的过程里会频繁修改目标函数和约束条件用建模语言比直接写矩阵要省力得多。3.3 核心代码骨架下面是我复现时使用的简化骨架重点展示模型层写法。为了压缩篇幅这里只列核心部分完整约束按同样模式补齐即可。import pyomo.environ as pyo model pyo.ConcreteModel() # 索引时段 T场景 S model.T pyo.RangeSet(0, 95) # 15分钟粒度96个时段 model.S pyo.RangeSet(0, 49) # 50个场景 # 参数场景概率、时段长度、消纳上限 model.pi pyo.Param(model.S, initializeprob_list) model.dt pyo.Param(initialize0.25) # 单位小时 model.L pyo.Param(model.T, initializelimit_list) # 变量 model.p_h pyo.Var(model.T, model.S, domainpyo.NonNegativeReals) model.p_pv_use pyo.Var(model.T, model.S, domainpyo.NonNegativeReals) model.q_turb pyo.Var(model.T, model.S, domainpyo.NonNegativeReals) model.q_spill pyo.Var(model.T, model.S, domainpyo.NonNegativeReals) model.V pyo.Var(model.T, model.S, domainpyo.NonNegativeReals) # 目标函数可消纳电量期望 def obj_rule(m): return sum( m.pi[s] * sum( (m.p_h[t, s] m.p_pv_use[t, s]) * m.dt for t in m.T ) for s in m.S ) model.obj pyo.Objective(ruleobj_rule, sensepyo.maximize)这只是开头真正求解前还需要加上水量平衡方程、库容上下限、水电出力上限、光伏消纳上限、系统消纳约束等。写约束时注意所有变量都必须有场景下标 s否则同一个变量在不同场景下被重复赋不同值模型就会出现不可行或错误的结果。4. 调试实录常见问题与排查方法4.1 模型不可行先查量纲和单位复现这个模型我遇到最多的问题就是模型直接不可行求解器返回 infeasible。第一次排查几乎都在约束和单位上。水利参数里天然来水单位是立方米每秒库容单位是万立方米发电出力单位是兆瓦三个单位混在一起稍有疏忽就出问题。我的处理原则是全部统一成 SI 基本单位加常用工程单位。流量统一用立方米每秒体积统一用立方米电量统一用兆瓦时时间统一用小时。库容换算时特别注意亿立方米和立方米之间的倍数关系96 个时段里任何一处单位错误差会被放大近百倍。建议在建模前把所有参数单位换算好再喂给求解器。4.2 场景太多算不动削减和测试策略刚开始我直接生成 500 个场景96 个时段模型变量数一下子超过十万Gurobi 虽然能跑但速度明显变慢。后来改成先用 20 个场景测试模型逻辑是否正确再逐步增加场景数到 100 个。这样做既能快速发现约束错误也能评估场景数量对结果的影响。如果场景数量必须很大可以用场景削减把 500 个场景聚合成 50 个典型场景。经验上对短期调度期望值模型来说50 到 100 个场景已经能得到很稳定的目标函数值再增加场景数对结果改进有限但计算耗时却会成倍上升。4.3 数据异常光伏和来水场景怎么清洗光伏预测数据偶尔会出现负值尤其是夜间时段误差为负时加上随机扰动后可能出现负的光伏出力这在物理上是不可能的。场景生成后我加了非负约束把小于 0 的 P_pv 直接归零。来水数据同样要保证非负否则水量平衡会出现负库容导致模型不可行。另外要注意场景削减后所有场景概率之和必须等于 1。如果概率没有归一化目标函数会被整体放大或缩小不同算例之间结果不可比。4.4 结果里同时出现弃水和弃光是bug还是物理现实我跑完第一版结果后发现有些时段上游电站弃水光伏同时也在弃光第一反应是模型写错了。后来仔细分析这是消纳上限约束下的正常现象。当光伏大发且消纳空间不足时系统无法同时接纳水电和光伏全部出力水电被迫少发上游来水只能弃掉光伏也要弃掉一部分。这不是模型错误而是互补系统在消纳受限时的真实表现。在论文结果分析里这种“被迫弃电”的情况恰恰说明模型考虑了消纳瓶颈比假设“所有发电都能上网”的理想模型更有实际意义。如果你复现出的结果完全没有弃电反而要检查是不是漏掉了消纳约束。4.5 求解器状态速查表调试过程中我把常见求解器状态整理成一张速查表方便快速定位问题方向。求解器状态含义优先排查方向Optimal求解成功检查结果是否合理对比多场景情况Infeasible模型无可行解先查单位换算再查约束方向是否矛盾最后查场景数据是否物理合理Unbounded目标函数无界检查是否有变量缺约束特别是出力变量缺少上限时常见Time limit exceeded达到时间上限减少场景数或切换到更快求解器必要时减少时段数测试Numerical issues数值稳定性问题参数量级尽量统一避免出现 1e8 和 1e-8 混用5. 复现之后还能怎么改三个实用扩展方向5.1 把风险偏好加进去从期望值到CVaR期望最大化是风险中性的它不会特别照顾那些最坏的场景。实际运行中调度员往往更关心极端场景下的消纳风险比如连续阴天加上来水偏枯时系统可消纳电量会大幅下降。可以在目标函数或约束里加入条件风险价值约束限制最坏一批场景的平均消纳电量不能低于某个阈值。CVaR 约束可以线性化不会显著增加求解难度是学术论文里很自然的扩展方向。5.2 从日前模型走向滚动调度日前模型把 24 小时看作一个静态优化窗口但实际运行中光伏预测会逐小时更新来水预报也会修正。把日前的期望优化模型改造成模型预测控制框架每个小时滚动求解未来若干小时的计划只执行第一个时段的决策下个小时重新优化可以显著提高对预测误差的适应性。梯级水光互补系统非常适合作这样的滚动调度研究也是近年文献的一个热点。5.3 模型代码工程的建议最后说点个人体会。复现这种跨学科的优化模型最大的坑通常不在算法理论而在数据准备和代码组织。我建议先把确定性版本跑通再把不确定性场景加进去先把目标函数只写成水电电量再加光伏先不考虑禁投约束再加复杂约束。每加一块都跑一遍并记录结果这样出了问题能精准定位到是哪个模块引入的。另一个建议是做好随机种子管理。场景生成时固定随机种子保证每个人跑出来的场景一样这样论文复现时结果可对得上。否则每次运行结果不同很难判断代码改对没有。做完这些模型基本就能稳定输出了。
阅读完成 · 觉得有帮助?