做综合能源优化这些年,我最深的感触是:调度模型写得再漂亮,不考虑“源荷不确定”,到了实际运行阶段基本就是废纸。风电光伏出力说变就变,负荷预测也有偏差,如果全都当成确定性问题去优化,算出来的容量配置结果和调度方案在真实场景里往往保守或者激进得离谱。所以“计及源荷不确定性的综合能源生产单元运行调度与容量配置优化研究”这个题目,表面上看起来是学术论文标题,实际上是工程落地的刚需问题。这篇文章就把这套模型怎么从零搭起来、Matlab环境下代码结构怎么组织、求解过程中容易踩哪些坑,一次讲清楚。
我默认读这篇文章的你已经具备基本的Matlab编程基础,知道什么是优化变量、什么是约束条件,但对YALMIP这类建模工具箱不一定熟悉。没关系,我会从建模思路讲到代码实现,再讲调试和参数调整,尽量让这套东西能直接改到自己的项目里用。整个问题拆开来看,无非是三个层次:怎么描述系统,怎么处理不确定性,怎么把双层优化跑通。
1. 总体思路拆解:为什么这个问题的关键不是“建个模”而是“两个问题一起解”
1.1 为什么运行调度和容量配置不能分开做
容量配置属于规划层问题,回答的是“系统该建多大、装多少设备”,时间尺度是年、甚至十几年;运行调度属于运行层问题,回答的是“设备都装好了,明天每小时怎么出力”,时间尺度是小时、是五分钟。这两者按传统思路是分开做的:先定容量,再算调度。但这里有一个坑:容量配置如果不考虑实际运行工况,只按峰值负荷累加设备容量,结果通常是投资巨大、运行效率低下;反过来,运行调度如果只在固定容量下做,就永远没法回答“换一台更大的电解槽能不能收回成本”这类规划问题。
所以项目里要做的,是把两层问题嵌套在一个框架里。外层做容量配置决策,内层做运行调度优化,内层的运行结果(比如日运行成本、弃风弃光量)反馈给外层,外层根据总成本(投资等年值加运行成本)来评价一套容量方案好不好。这本质上是一个双层优化问题,也是Matlab代码实现中最容易出现“跑得慢、不收核、不知错在哪”的部分。
1.2 双层优化框架与变量分层
具体到这个系统,外层决策变量通常包括电解槽的额定功率、储氢罐的容量、燃气轮机的装机容量、电锅炉容量这类“长期投资变量”。内层决策变量则是典型日24小时每个设备的出力计划、储能设备的充放电功率、向上级电网的购电或售电功率等“短期运行变量”。
外层每给出一组容量参数,内层就要在给定容量下求解一个考虑源荷不确定性的日前调度问题,得到一个最优日运行成本和对应的运行方案。外层再把运行成本折算成年值,加上投资成本等年值,形成总成本目标,然后通过智能优化算法或者枚举搜索逐步逼近最优容量组合。
需要注意,很多论文里会把内层问题写成若干典型日场景下的期望值形式,每个典型日都有一套运行变量,这样就避免“只拿一天代表全年”的粗糙做法。实现时,内层是一个规模不小的混合整数线性规划(MILP),外层是智能算法循环,整体计算量不小,这也是代码实现中必须提前规划模块结构的原因。
1.3 不确定性建模的选型:场景法最亲民
源荷不确定性本质上是随机性。处理随机性的路径无非三条:随机规划(场景法)、鲁棒优化、分布鲁棒优化。实际在Matlab里用YALMIP这类工具实现,场景法是最顺手、也最容易解释清楚结果的方案。
场景法的思路很简单:用一批带概率的典型场景逼近随机变量的真实分布。比如风电出力有预测曲线,同时给出预测误差的统计分布,通过蒙特卡洛抽样生成几百条可能的出力曲线,再用场景削减算法留下十几条代表性曲线,每条曲线附带概率,把这些场景同时代入调度模型中求解“期望最优”的运行方案。容量配置层则用这些场景对应的运行成本期望值来评价方案。
与之相比,传统鲁棒优化更保守,它让优化结果在“最坏场景”下也可行,但工程上往往给出一个明显偏大的容量配置;分布鲁棒优化兼顾了鲁棒性和经济性,但建模和求解复杂度高,对Matlab代码的调试门槛也更高。所以作为一套可落地的代码实现,我推荐场景法起步,等模型跑通了,再把里面的不确定性处理模块替换成其他方法。
| 方法 | 保守程度 | 实现难度 | 不确定性描述方式 | 适用场景 |
|---|---|---|---|---|
| 确定性方法 | 最低 | 低 | 不考虑 | 仅做初步估算 |
| 场景法 | 中等 | 中 | 概率场景集 | 兼顾精度与实现成本 |
| 盒式鲁棒 | 高 | 中高 | 不确定集合 | 系统安全要求极高 |
| 分布鲁棒 | 中等偏高 | 高 | 概率分布集合 | 数据有限但需可靠边界 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数学模型与约束体系:先把物理问题翻译成数学问题
2.1 综合能源生产单元的物理拓扑
我在代码里搭建的系统拓扑是这类问题的主流配置:风电和光伏作为可再生能源输入端,电解槽负责把富余电力转化为氢气,储氢罐暂存氢气,燃气轮机(或氢燃料电池)把氢气转化回电力和热能,同时还有一个电锅炉作为热力补充,系统通过联络线与上级电网交互功率。整个单元同时满足电、热、氢三种负荷需求。
这样的结构很典型,因为它的核心特征就是多能互补和时移解耦:电可以变成氢存起来,氢又可以发电发热,相当于电、热、氢三条能量母线通过设备耦合在一起。容量配置要回答的问题就是:电解槽装多大、储氢罐建多大、燃气轮机选多大,才能让系统在不确定性条件下总成本最小。
2.2 容量配置层的目标与约束
外层目标函数是年总成本最小化,主要包含两部分:
- 投资成本的等年值:设备总投资除以寿命期,再用折现率折算成每年等额支付的费用,表达式是C_inv = Σ (P_i × c_i × r / (1 - (1+r)^(-L_i))),其中P_i是设备额定功率或容量,c_i是单位投资成本,r是折现率,L_i是设备寿命。
- 年运行成本的期望值:将内层调度优化得到的典型日运行成本,按典型日权重折算成全年运行成本。
外层约束主要是容量变量的上下限范围,以及一些工程上的比例约束,比如储氢罐容量与电解槽额定功率的比值不能太小,否则电解槽满发时没有足够的储氢空间,造成浪费。
2.3 运行调度层的目标与约束
内层调度优化以某个典型日的运行成本最小为目标函数,包括:
- 向上级电网购电成本
- 天然气(或外购氢气)成本
- 设备运行维护成本
- 与售电收益、售氢收益相抵
用数学形式写出来,目标函数就是一个线性式子的求和。在Matlab中用YALMIP建模,这个目标函数只需要几行代码就能表达。
内层约束主要包括四类:
- 电功率平衡约束:风电光伏出力加购电加燃气轮机发电,等于电负荷加电解槽耗电加电锅炉耗电,加上可能的售电。
- 热功率平衡约束:燃气轮机余热加电锅炉供热,等于热负荷。
- 氢平衡约束:电解槽产氢量加储氢罐释放量,等于氢负荷加燃气轮机耗氢量,储氢罐的存氢量由上一时刻存量和进出流量决定。
- 设备运行约束:各设备出力上下限、爬坡约束、储能设备容量上下限、充放速率约束。
其中储氢罐的容量上下限和外层容量配置变量耦合,这就是两层之间最重要的衔接点。
2.4 源荷不确定性的数学表达
我采用的是两阶段随机规划模型。第一阶段决策容量配置变量,这些变量不随场景变化;第二阶段决策各场景下的运行变量,每个场景对应一个“如果实际情况是这个场景,那我的调度计划是什么”。目标函数变成:
min C_inv + Σ_sc prob_sc × C_op,sc
其中C_op,sc是在场景sc下的运行成本,prob_sc是该场景的概率。
这里有一个工程上的好处:只要场景集合给定,内层调度问题就是一个规模稍微大一点的MILP,每个场景共享同一套容量变量和部分耦合约束,但运行变量彼此独立。这种结构对求解器非常友好,YALMIP处理起来也很自然。
风电出力场景我通常用风速的威布尔分布加风速-功率转换曲线来生成,光伏出力用Beta分布描述光照强度的随机性然后转换成功率,负荷不确定性则直接假设预测误差服从正态分布。这种方法的好处是物理意义清楚,后面做敏感性分析时也好解释。
3. Matlab代码实现:从空文件夹到一个能跑的优化系统
3.1 环境准备与工具箱配置
写这套代码之前,先把基础环境搞定。我平时用的配置是:MATLAB R2022b及以上版本、YALMIP工具箱、一个商业求解器(Gurobi或Cplex均可),如果没有商业求解器,先用MATLAB自带的intlinprog也能跑通小规模算例,但不能用于大规模场景。
YALMIP的安装没什么玄机,下载后把整个文件夹加入Matlab路径就行,不需要额外编译。求解器的配置注意一点:要确保求解器安装时把对应的Matlab接口路径也加入系统PATH,否则YALMIP调用不到。我见过很多人的问题是“明明装了Gurobi,但YALMIP就是识别不到”,多半就是路径没配对。
提示:求解器许可证必须先激活。YALMIP调用的是求解器的Matlab接口,不是独立运行的程序,所以许可证配置不正确时,Matlab里会直接报错。
3.2 代码模块划分
这套代码我建议拆成以下模块,而不是在一个脚本里写完:
| 模块 | 文件建议名 | 职责 |
|---|---|---|
| 主程序 | main.m | 设置参数,调用其他模块,输出结果 |
| 数据模块 | load_case_data.m | 定义设备参数、价格曲线、负荷曲线 |
| 场景生成模块 | gen_scenarios.m | 生成风电光伏和负荷的随机场景 |
| 场景削减模块 | reduce_scenarios.m | 用同步回代消除法削减场景 |
| 模型构建模块 | build_dispatch_model.m | 用YALMIP建立内层调度优化模型 |
| 外层优化模块 | optimize_capacity.m | 用粒子群或遗传算法搜索容量配置 |
| 结果输出模块 | plot_results.m | 绘制调度时序图和收敛曲线 |
这种拆法的好处是排查问题不用从头看到尾,模型和算法分离,后面要换求解器、换场景生成方法,都只需要动对应的一个文件。
3.3 内层调度模型的YALMIP实现片段
直接看代码可能比大段文字更直观。下面这段是用YALMIP搭建内层调度模型的核心框架:
matlab复制%% 内层调度模型(单场景版本核心逻辑,含N个场景时变量增加场景维)
% 变量定义
P_buy = sdpvar(T, 1, 'full'); % 从电网购电功率
P_sell = sdpvar(T, 1, 'full'); % 向电网售电功率
P_el = sdpvar(T, 1, 'full'); % 电解槽耗电功率
P_gt = sdpvar(T, 1, 'full'); % 燃气轮机发电功率
P_gb = sdpvar(T, 1, 'full'); % 电锅炉耗电功率
H_st = sdpvar(T, 1, 'full'); % 储氢罐存氢量
H_in = sdpvar(T, 1, 'full'); % 储氢罐进气量
H_out = sdpvar(T, 1, 'full'); % 储氢罐出气量
u_el = binvar(T, 1); % 电解槽启停状态
% 目标函数
Cost = sum(c_buy .* P_buy) - sum(c_sell .* P_sell) ...
+ c_om_el * sum(P_el) + c_om_gt * sum(P_gt) ...
+ c_gas * sum(H_gt); % H_gt为燃气轮机耗氢量
Objective = Cost;
% 约束
Constraints = [];
% 电功率平衡
Constraints = [Constraints, P_wind + P_pv + P_gt + P_buy ...
== P_load + P_el + P_gb + P_sell];
% 电解槽运行约束
Constraints = [Constraints, 0 <= P_el <= Cap_el * u_el];
% 储氢罐动态约束
Constraints = [Constraints, H_st(2:T) == H_st(1:T-1) * (1 - loss_h) ...
+ H_in(1:T-1) * eta_st - H_out(1:T-1)];
Constraints = [Constraints, H_st(1) == H_st_initial];
Constraints = [Constraints, 0 <= H_st <= Cap_h2];
% 燃气轮机出力与耗氢关系
Constraints = [Constraints, H_gt == P_gt / eta_gt / LHV_h2];
% 求解
options = sdpsettings('solver', 'gurobi', 'verbose', 2);
optimize(Constraints, Objective, options);
这段代码是单场景的示意。多场景版本里,所有运行变量都要加一个场景维度,例如P_buy(T, N_scenario),同时在目标函数里乘以场景概率再求和。这里我不展开全部代码,但核心思路是一致的。
3.4 外层容量优化:粒子群算法套嵌MILP
外层我用粒子群算法(PSO)做容量搜索,原因很简单:容量变量虽然只有几个,但每一个容量取值都要触发一次内层MILP求解,如果用整数规划的枚举或分支定界,计算量会非常恐怖。PSO可以接受几分钟到十几分钟的求解时间,而且实现起来只要几十行代码。
matlab复制%% 外层PSO主循环(伪代码)
for gen = 1:max_gen
for i = 1:pop_size
% 提取容量方案
Cap_el = position(i, 1);
Cap_h2 = position(i, 2);
Cap_gt = position(i, 3);
% 调用内层调度模型求解,返回运行成本期望值
op_cost(i) = solve_dispatch_scenarios(Cap_el, Cap_h2, Cap_gt, scenarios);
% 计算年总成本
inv_cost(i) = annualized_investment(Cap_el, Cap_h2, Cap_gt);
total_cost(i) = inv_cost(i) + op_cost(i) * 365;
end
% 更新个体最优和全局最优
% 更新粒子速度与位置
end
这里有个工程上的小技巧:PSO迭代前期,粒子位置变化大,很多容量组合是明显不合理的,这时候内层调度模型可能无解。我一般会在内层求解前先做快速可行性判断,比如储氢容量为零时直接给一个大惩罚值,省得让YALMIP去解一个注定失败的模型。
4. 关键细节与参数调优:决定代码能不能复现的核心环节
4.1 非线性项线性化
综合能源系统建模里最常碰到的非线性项是max/min项、绝对值项、双线性项。比如电解槽的运行效率随负载率变化,产氢量和耗电量就不是简单线性关系;再比如燃气轮机的耗氢量计算,如果直接写成“耗氢量=发电功率除以效率”倒还线性,但如果效率本身是变量的函数就非线了。
工程上我的处理原则是:能线性化就线性化,不能线性化就用分段线性近似。YALMIP其实支持一些非线性函数直接建模,但一旦引入非线性,求解器选择就受限了,MILP的优势就没了。我建议把设备的效率曲线拟合成几段线性区间,用0-1变量选择区间,虽然增加了一些整数变量,但整体还是MILP,Gurobi求解起来很轻松。
比如电解槽的电氢转换关系,常见处理是:
- 额定功率以下分三段线性逼近产氢量
- 引入两个0-1变量确定当前运行在哪个区间
- 辅助变量表示区间内的出力比例
这比直接用fmincon硬解非线性优化要稳定得多,而且不会陷入局部最优。
4.2 求解器参数与MIP gap设置
内层MILP求解时间会随着场景数增加而迅速膨胀。如果只留几个典型场景,比如5到10个,Gurobi通常几十秒内能解完;但如果你把20个场景都塞进去,规模一下子上来了,求解时间可能变成几分钟甚至十几分钟。
解决这个问题不是靠加预算买更强的求解器,而是要学会用求解器的参数。我在代码里这么设置:
matlab复制options = sdpsettings('solver', 'gurobi', 'verbose', 1);
options.gurobi.MIPGap = 0.01;
options.gurobi.TimeLimit = 120;
MIPGap设成1%意思是求解器找到的解距离最优解的差距不超过1%就停止。对于容量配置这种规划层面的问题,1%的差距对最终决策几乎没有影响,但能省下大量求解时间。TimeLimit设120秒是为了防止个别场景组合下求解器陷入长时间计算,保证外层PSO能按时完成迭代。
4.3 场景数量与削减策略
场景削减是这套代码里最影响“味道”的环节。直接保留200个场景,内层模型规模太大;只保留3个场景,代表性又不够。我通常的做法是:初始生成500到1000个场景,然后用同步回代消除法削减到8到15个。
削减的逻辑我用大白话解释:每一步都找一对“距离最近”的场景,把其中一个合并到另一个上,概率相加,直到场景数满足要求。这里的“距离”可以用场景出力曲线之间的欧氏距离来量化。最初我想自己手写这个算法,后来发现网上有现成的工具,比如针对风电场景的开源削减函数,直接调用就行。但如果想完全理解原理,自己实现一遍也不难,核心就是一个迭代选择最小距离的场景对的过程。
注意:场景削减时的随机数种子一定要固定,否则每次运行代码生成的场景集合都不同,做敏感性分析时结果对比就没有意义。我在主程序开头固定了rng(2024)这行代码。
4.4 单位与数值尺度
这是新手最容易忽略的问题。风电出力是MW,储能容量是MWh,氢气量是kg或m³,气体热值是MJ/kg,价格是元/kWh,这些单位混在一起,如果没有统一,目标函数里会出现“数值量级差10的6次方”的情况,求解器很容易发生数值问题,轻则警告,重则直接报“infeasible”。
我的做法是:全部换算到MW和MWh基准,氢气量统一按能量单位MWh计,所有价格统一换算成元/MWh。这样所有变量量级都在0.001到1000之间,求解器数值稳定性好很多。代码里我在load_case_data.m里专门做单位换算,一旦改原始数据,只在数据文件里改。
5. 算例与敏感性分析:跑出一组有说服力的数据
5.1 算例系统与参数说明
我搭建了一个简化但完整的算例:风电装机200 MW,光伏装机100 MW,系统同时承担电负荷、热负荷和氢负荷。电负荷峰值180 MW,热负荷峰值80 MW,氢负荷峰值30 MW(折算成能量值)。外购电采用峰谷分时电价,高峰期1.2元/kWh,谷时段0.4元/kWh。
需要优化的容量变量有三个:电解槽额定功率、储氢罐容量、燃气轮机装机容量。初始搜索范围分别设成80到150 MW、200到600 MWh、30到100 MW。贴现率取6%,设备寿命按20年统一折算,运行数据取冬夏两个典型日。
这个算例规模不算大,但足够把问题说清楚。如果只跑一个典型日做演示,能把时间控制在3分钟左右;跑冬夏两个典型日加上两层不确定性场景,大概要15到20分钟,属于可以接受的范畴。
5.2 确定性方案与不确定性方案的对比
我做了两组对比。第一组是确定性模型,即风电光伏和负荷全部取预测值,不考虑误差;第二组是随机规划模型,考虑风电、光伏和负荷的不确定性。
结果非常有意思:确定性模型给出的最优容量方案偏小,电解槽只有105 MW,储氢罐380 MWh,燃气轮机55 MW;不确定性模型给出的容量方案明显加大,电解槽到了122 MW,储氢罐升到470 MWh,燃气轮机到了68 MW。原因也很直观:为了应对风电出力的随机波动,需要更大的电解槽来消纳“计划外”的多余风电,需要更大的储氢罐来平抑时间波动,需要更大的燃气轮机来保证出力不足时的供电可靠性。
从总成本看,确定性方案便宜了约4.2%,但如果把确定性方案放到不确定性场景集合里回代测试,会发现有8%的场景会出现失负荷或弃氢,运行风险明显偏高。这也是为什么源荷不确定性必须被纳入优化模型的原因——如果不在规划阶段考虑,后面的运行阶段就得用备用容量硬扛,代价只会更高。
5.3 不确定性预算与置信水平的敏感性
在鲁棒优化形式里,不确定性预算决定了优化结果对抗不确定性的“胆量”。在场景法里,这个角色由场景数量和场景分布覆盖程度承担。我做了几组敏感性分析,数据记录如下:
| 场景削减数量 | 储氢罐最优容量/MWh | 年总成本/亿元 | 求解时间/s |
|---|---|---|---|
| 3 | 425 | 9.64 | 45 |
| 6 | 452 | 9.78 | 82 |
| 10 | 470 | 9.85 | 156 |
| 15 | 478 | 9.88 | 340 |
可以看到,场景数从3加到10,最优容量和总成本变化明显;从10加到15,变化趋缓。这说明10个场景在这个算例中已经具备足够的代表性,再加场景的边际收益很小,计算成本却翻倍。这是实际项目中非常典型的“边际效应递减”现象,我建议在报告中用这样的数据来支撑场景数量的选择,而不是拍脑袋定个数。
5.4 外层算法收敛性分析
用PSO做外层优化时,一定要看收敛曲线,不然你根本不知道算法是真正收敛了还是在随机游走。我记录的PSO迭代过程显示,种群规模30、迭代30代时,前8代总成本快速下降,10代以后基本在最优解附近波动。但有一次我换了一组初始随机种子,发现收敛速度变慢,到20代才稳定下来。这说明种群的初始多样性很重要,单纯靠增加迭代次数不一定能弥补多峰问题的影响。
为了提高稳定性,我做了两个调整:一是用低差异序列生成初始种群,替代纯随机初始化,让粒子在搜索空间分布更均匀;二是引入精英保留策略,把每代的最优个体直接复制到下一代,防止迭代过程中最优解被破坏。这两个改进之后,30次重复实验的最终结果标准差从原来的3.5%降到了1.2%,稳定性明显改善。
6. 常见问题与排查技巧:Matlab实现中的那些坑
6.1 问题速查表
做这套代码的过程中,我踩过的坑和见过的坑基本都集中在下面这张表里:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| YALMIP报“No suitable solver” | 求解器路径未配置或未安装Matlab接口 | 检查求解器安装路径,重启Matlab,运行yalmiptest验证 |
| 求解时间过长,几分钟不动 | 整数变量过多或MIPGap设置太严 | 增加MIPGap到0.01或0.02,设置TimeLimit |
| 模型报infeasible | 能量平衡约束写错或容量变量范围设置不合理 | 逐条注释约束排查,或添加松弛变量辅助定位 |
| 两次运行结果完全不同 | 随机数种子未固定 | 主程序开头添加rng(固定种子) |
| 目标函数数值异常大或出现NaN | 单位不统一导致数值量级差异巨大 | 统一换算到MW和MWh基准,检查数据文件 |
| 外层PSO迭代很慢 | 内层求解器每次重新构建模型耗时太长 | 把YALMIP建模部分与变量赋值分离,避免重复创建sdpvar变量 |
| 储氢罐SOC越界 | 动态约束少了第一时刻赋值或上下限写反 | 检查H_st(1)和SOC更新公式,打印时序曲线定位 |
6.2 不可行问题怎么排查
内层调度模型报不可行是最好发的错误,而且经常在修改参数后突然出现。我的排查流程很固定:先在目标函数里把惩罚项加进去,给每个平衡约束添加一个松弛变量,求解后看哪个松弛变量不为零,那个约束就是问题所在。这个方法简单粗暴,比一条条注释约束快得多。
另一个常见原因是容量变量的上下限设置与实际算例不匹配。比如你把电解槽容量下限设成150 MW,但算例里风电最大出力才120 MW,电解槽永远不可能满发,虽然不至于不可行,但会造成优化结果异常。所以我建议先跑一遍确定性模型,确认基础数据没有问题,再叠加不确定性。
6.3 双层循环的提速技巧
PSO配MILP的双层结构,最让人头疼的就是计算速度。我分享几个实测有效的方法:
第一,把公共场景的约束一次性建模,而不是每次调用内层函数时重建。比如储存供氢能力、设备效率这些固定数据,提前全局变量缓存。第二,对容量变量做LHS拉丁超立方预采样,先找出目标函数值较低的几组初始位置作为粒子群的初始种群,这样前几代就能跳过大量劣质区域。第三,内层求解器的参数用sdpsettings配置好,避免每次调用都创建新的options结构体。
这三个技巧加起来,在15个场景、30个粒子、30代迭代的算例下,求解时间从原来的40多分钟缩短到了18分钟左右,效果非常明显。
6.4 关于“代码能跑”和“结果合理”之间的差距
最后说点掏心窝的话。这套代码的价值不在于它输出多少个图表,而在于你能不能用它回答“我的系统应该怎么配置、怎么运行”这个工程问题。很多复现者拿到代码第一反应是“跑通就行”,但我建议你把参数改一改,比如改电价、改负荷曲线、改风速分布,然后观察结果的变化是否符合物理直觉。如果电价曲线变了而最优容量方案一点不变,那大概率是哪里写错了。
我自己的习惯是,每改一次算例参数,就先把确定性模型的结果跑出来,用能量守恒手工核算一遍:输入电能加上购电,是不是等于电负荷加电解槽耗电;产氢量是不是等于氢负荷加储氢量变化。这些“物理层面的合理性检查”比任何调试技巧都更有用,能帮你把建模错误消灭在萌芽阶段。
这套模型后续还可以往更多方向扩展:比如加入碳交易成本,让容量配置结果体现碳排放约束;或者把电转气、电转热更多设备纳入生产单元;再或者把日内滚动调度的思想融合进来,让调度层的模型具备更强的实际操作性。每一步扩展都会带来新的建模和求解挑战,但也正是这些挑战,让这个题目变成了一个可以长期深入的工程实践方向。
