计及氢能的综合能源优化调度研究,这阵子在课题组里几乎成了标配方向。起因也很直接:传统综合能源系统(IES)里折腾来折腾去,就是燃气轮机、电锅炉、储电蓄热那几样,碳减排的空间越来越小,而氢能作为一种“既能当燃料烧、又能存下来跨时段用、还能通过燃料电池反哺电能”的多面手载体,正好把电力系统、热力系统和天然气系统的耦合又抬高了一个维度。我前后帮几位同学搭过这类模型,从初版只做电热联供,到后来一步步把电解槽、燃料电池、储氢罐塞进调度框架,Matlab代码也从几百行膨胀到上千行。这篇文章就把这个项目从建模到落地的完整链路拆开讲清楚,包括设备建模、约束设计、求解器选型、代码组织方式,以及跑算例时最容易踩的那些坑,给后面做类似计及氢能的IES优化调度的同学一条可以照着走的路。
1. 为什么偏偏是氢能:综合能源调度的“新变量”与建模动机
1.1 电热耦合的旧格局,到底卡在哪了
传统IES的调度模型,核心思路很简单:以系统运行成本最小为目标,同时满足电、热负荷需求以及各类设备的运行约束。设备层面通常包括风力发电、光伏、微型燃气轮机(MT)、燃气锅炉(GB)、电锅炉(EB)、蓄电池(ESS)、蓄热罐(TSS)。这种结构有一个天然短板——能量的时间转移能力太弱。蓄电池能存电,但容量有限、寿命衰减快;蓄热罐能存热,但热损失大、做工粗糙;燃气轮机倒是能发电又能供热,但单位碳排放高,而且电和热的产出比并不总能刚好匹配负荷比例。
在大量风电、光伏接入之后,弃风弃光问题进一步放大了这种缺陷:中午光伏大发,电负荷却处于低谷,蓄电池很快就充满,多余的电只能切掉;到了傍晚负荷高峰,光伏归零,又得靠燃气轮机顶上去。说白了,系统缺的不是电量,而是“把能量从一个时段搬到另一个时段”的能力。
氢能恰恰补上了这一环。它的逻辑可以这样理解:在电能充裕时段启动电解槽,把水电解成氢气和氧气,氢气经过压缩后存入储氢罐;等到电能紧张或需要清洁燃料时,再让储氢罐放氢,通过燃料电池发电,或者直接供应给加氢站等氢负荷。整个过程看下来,氢能相当于一个跨时段、跨能源品种的双向储能和转换枢纽,既能消纳可再生能源,又能在负荷高峰期反哺电力,同时产出的余热还能进入热力系统。
1.2 氢能的“三重身份”:负荷、电源、储能
从调度建模的角度看,氢能设备最让人头疼的地方在于它们的身份一直在切换。电解槽是“负荷”,而且是出力可调的柔性负荷;燃料电池是“电源”,但它的燃料来源是储氢罐;储氢罐本身又是“储能设备”,存在容量上限、压力动态和调度周期内始末状态约束。一个设备集群同时具备负荷、电源、储能三种属性,就使得系统耦合强度远超传统电热联供。
举一个具体场景来说明:某工业园区配了一个 500kW 的电解槽、一套 200kW 的燃料电池和容量 200kg 的储氢系统。白天光伏出力高,系统让电解槽满功率运行,假设效率为 70%,那么消耗 500kW 电功率可以产生大约 10.5kg/h 的氢气。到了晚间,负荷峰值来临,燃料电池投入,按额定 200kW 放电,每小时消耗氢气大约 6kg/h。这样算下来,白天产氢 8 小时就是 84kg,晚间供能 4 小时消耗 24kg,罐容仍有富余。这就是氢能在日内调度中的典型价值:把光伏的“垃圾电”转化为负荷高峰期的“救援电”。
1.3 研究对象:典型的“风-光-气-氢”综合能源系统架构
本文涉及的Matlab代码实现,对应的系统架构是当前论文里最经典的一类:输入侧包含风力发电、光伏发电、上级电网购电、天然气网购气;转换侧包含燃气轮机、燃气锅炉、电锅炉、电解槽、燃料电池;存储侧包含蓄电池、蓄热罐、储氢罐;负荷侧则包含电负荷、热负荷和氢负荷(如加氢站需求)。
这个架构不是随意拼凑的,它覆盖了学术界和工程界最关心的三个关键场景:可再生能源消纳、多能互补支撑、以及低碳化运行。调度目标通常取系统日运行总成本最小化,包括购电成本、购气成本、设备启停成本、运维成本,再加上弃风弃光惩罚成本。如果论文偏低碳方向,还会把碳交易成本或碳排放惩罚项引入目标函数。
从Matlab代码实现的角度来说,这个架构的建模挑战集中在三点:多能流(电、热、气、氢)在母线处的能量平衡怎么表达;设备的非线性特性(如电解槽的效率曲线、燃料电池的电热比)如何线性化;启停状态这类离散变量如何嵌入连续优化框架。下面的章节就按建模→求解→代码→踩坑的顺序逐个展开。
2. 氢能子系统的设备建模与能量耦合逻辑
2.1 电解槽:电到氢的转换效率不是定值
电解槽是氢能系统里最关键也最容易建模翻车的设备。很多初学者直接用一个固定效率 n_el 乘以输入电功率得到产氢速率,这在概念上能跑通,但离实际情况偏差较大。碱性电解槽(技术最成熟、文献中用得最多)的运行效率会随着负载率变化,一般在 60%~80% 之间浮动;低负载率时效率明显下降,高负载率时效率接近额定点。
为了保持MILP的线性结构,代码里通常采用分段线性化处理,而不是直接引入效率曲线函数。具体操作是:将电解槽的输入功率区间 [P_el_min, P_el_max] 等分为 N 段,每段对应一个效率系数,再引入连续变量和二进制变量标识运行区间。这样既保证了设备模型的准确性,又不引入非线性项,Cplex/Gurobi 求解效率基本不受影响。
另外,电解槽还有一个被很多人忽略的约束——最小稳定运行功率。出于电化学性能和设备保护考虑,电解槽不能长期在很低的功率下运行,一般要求负载率不低于 20%~30%。在代码里要写成:
P_el(t) >= P_el_min × z_el(t)
P_el(t) <= P_el_max × z_el(t)
z_el(t) ∈ {0,1}
这个 z_el 就是启停状态变量。如果漏掉这条约束,求解器完全可能给出一个 5% 负载率的“最优解”,实际工程中根本不可行。
2.2 燃料电池:不光发电,还附带产热
燃料电池在综合能源系统里的建模逻辑和燃气轮机类似:输入氢气,输出电力和高温余热。比较常用的模型是质子交换膜燃料电池(PEMFC),单机容量从几十千瓦到几百千瓦不等,发电效率大致在 40%~55%,热电联合效率可以达到 80% 以上。
调度模型里,燃料电池的核心关系有两个。第一个是氢燃料消耗与发电功率的关系:
F_fc(t) = P_fc(t) / (η_fc × LHV_H2)
其中 LHV_H2 是氢气的低位热值,一般取 33.33 kWh/kg;η_fc 是发电效率;F_fc 是每小时消耗的氢气量。第二个是热电联产关系:燃料电池在发电时产生的余热可以通过换热装置进入热力系统,满足一部分热负荷。代码里通常引入一个热电比 β_fc,让产热量 Q_fc(t) = β_fc × P_fc(t),然后接入热母线平衡方程。
这里尤其提醒:不要忘记燃料电池也有最小出力限制和启停逻辑。许多论文为了简化,只用一台等值机组表示整个燃料电池站,这没错,但最小技术出力约束一定要保留,否则调度结果会在“额定满发”和“完全停机”之间跳变,现实中没有任何一台机组敢这么干。
2.3 储氢罐:跨时段耦合约束的核心
储氢罐的作用相当于蓄电池,只不过存储量以质量千克计,状态变量是氢气的存量。在Matlab里常用连续变量 S_tank(t) 表示 t 时段末储氢罐内的氢气量,相邻时段的状态转移方程写作:
S_tank(t) = S_tank(t-1) + [F_el_in(t) × η_in - F_fc_out(t) / η_out - F_load(t)] × Δt
其中 F_el_in 是电解槽注入储氢罐的氢流量,F_fc_out 是燃料电池从储氢罐提取的氢流量,F_load 是直接供给加氢站等氢负荷的量,η_in、η_out 是充放过程中的压缩/减压损耗系数,Δt 是调度时段步长,通常取 1 小时。
储氢罐的约束相对简明:容量上下限约束 0 ≤ S_tank(t) ≤ S_tank_max,以及调度周期始末状态约束 S_tank(0) = S_tank(T),目的是让氢储能系统在一天的运行结束后回到初始状态,这样才能做跨日滚动调度。很多新手会忽略始末状态约束,最后求出来一个“把罐子里氢用光”的最优解,从数学上没错,但工程上完全失去了可持续调度的意义。
2.4 系统层面的能量平衡:三条母线串起整个调度模型
把氢能设备放进IES之后,系统平衡约束就变成四条:电母线平衡、热母线平衡、天然气母线平衡、氢母线平衡。电母线方程是典型的耦合点:
P_wind(t) + P_pv(t) + P_MT(t) + P_fc(t) + P_grid_buy(t) + P_ess_dis(t)
= P_el_load(t) + P_eb(t) + P_el(t) + P_ess_ch(t) + P_grid_sell(t)
热母线平衡则要考虑燃气轮机的余热回收、燃气锅炉、电锅炉以及燃料电池产热的共同出力,总供热量等于热负荷需求(部分模型会允许少量弃热)。氢气母线平衡相对简单,是一条流量平衡关系:
F_tank_in(t) + F_supply(t) = F_load(t) + F_tank_out(t)
这里的四条母线方程是整个优化调度模型的数据中枢,Matlab代码中所有设备变量最终都要汇入这四条母线约束里。如果调试阶段算例出现能量不守恒的问题,95% 是这四条约束的符号写反了,后面第 5 节会专门讲这个排查点。
3. 优化目标与约束条件的数学表达:一个可求解的MILP框架
3.1 目标函数:成本最小化之外的三个“隐藏项”
最经典的IES调度目标函数是系统日运行成本最小化,展开来包含以下四类成本:
- 购能成本:向上级电网购电费用、从天然气网购气费用,气价通常按体积或质量计价,统一换算成天然气能耗(kW)费用。
- 设备运行维护成本:燃气轮机、电锅炉、电解槽、燃料电池、蓄电池、储氢罐各自的运维成本,通常按出力或吞吐量的线性函数折算。
- 启停成本:针对燃气轮机和电解槽,每次启动会产生额外成本或寿命损耗,建模时在目标函数中加入启动变量对应的惩罚项,同时配合逻辑约束强制变量取值合理。
- 弃风弃光惩罚成本与碳成本:前者用单位弃能量惩罚价格乘上弃风弃光量;后者在部分低碳模型里有单独的碳排放配额约束或碳税成本项。
一个关键的“隐藏项”是:启停成本不写进目标函数,而只写启停逻辑约束,会导致模型出现频繁启停的病态解。因为如果不加启停成本,MILP求解器会认为每小时自由切换启停状态没有代价,于是给出一个技术上荒谬的调度方案。这个问题在我协助复现的多个案例里出现过,具体放在第5节展开。
3.2 约束条件的松紧设计:什么该硬,什么该软
约束设计直接决定了模型的可解性,也反映了研究者对物理系统的理解深度。以我经验,有五类约束必须写成硬约束,不得松弛:
- 四条能量母线平衡约束(上文已列);
- 设备出力上下限约束,含启停状态,如燃气轮机和电解槽的出力范围;
- 爬坡约束:燃气轮机、电解槽的相邻时段出力变化率限制;
- 储氢罐、蓄电池的容量上下限和充放能速率约束;
- 调度周期始末储能状态一致约束。
有两类约束可以做成软约束,通过加入松弛变量和惩罚项处理:一类是氢负荷、电负荷在极端场景下的少量削减(切负荷成本极高,但允许发生),另一类是热负荷的微小弃热或者不足(用惩罚项约束)。
需要注意的是:对蓄电池和储氢罐这些“储能类”设备,充放电/充放氢状态的互斥性是一个常见的建模深水区。很多初学者写出的约束是“蓄电池充电功率 × 放电功率 ≤ 0”,这在MILP里是没法直接表达的。正确姿势是引入两个二进制变量 z_ch 和 z_dis,然后加约束:
0 ≤ P_ch(t) ≤ P_ch_max × z_ch(t)
0 ≤ P_dis(t) ≤ P_dis_max × z_dis(t)
z_ch(t) + z_dis(t) ≤ 1
三组约束合在一起,才能严格表达“同一时刻充放电互斥”。这部分逻辑虽有技巧门槛,但在Yalmip框架里写起来会很顺手。
3.3 非线性环节的线性化:把“不好解”变成“能解”
氢能设备的引入会在模型里带进三类典型的非线性环节:
- 电解槽效率随负载率变化(第2.1节);
- 燃料电池的氢耗与发电功率之间,部分文献写成功率的一次或二次关系;
- 天然气管网的流量压强非线性(单点模型通常忽略,网络模型需要处理)。
在Matlab代码实现里,主流策略是全部线性化后调用MILP求解器。二次效率曲线用分段线性逼近替代;功耗关系用分段线性函数;压强非线性的处理在单点IES里直接不做,只保留等值机组模型。这样做有一个隐性收益:MILP模型具备全局收敛性,不像MINLP那样依赖初始值,这对做科研复现和参数敏感性分析来说非常重要。
3.4 求解器选型:Yalmip+Cplex/Gurobi为什么是默认组合
Matlab下做优化调度建模,目前最舒服的方式是Yalmip工具箱加Cplex或Gurobi求解器。Yalmip提供了一套人性化的建模语法,可以很容易地把变量定义、约束集合和目标函数以接近数学表达式的方式写出来,然后自动转换成求解器需要的标准格式。
我个人的经验是:默认优先用Gurobi,只有在Gurobi不可用或规模足够小的时候才退回Cplex。两者的差别在几百个变量的小算例里几乎看不出来,但当调度周期从24小时扩展到168小时型,或设备数量翻倍、约束条数上千之后,Gurobi的求解速度和内存稳定性会明显好一截。这里注意一点:不管用哪个求解器,都要在Yalmip里显式指定所在路径并确保授权已在Matlab环境下加载。这一步看起来琐碎,实际占了新手“跑不通”原因的六成以上。
4. Matlab代码实现框架:从数据结构到算例复现
4.1 代码目录结构与数据输入模块
这个项目的Matlab代码不算特别长,但模块界限必须清晰,否则后期加设备、改场景时会非常痛苦。一个可行的目录组织如下:
IES_Hydrogen/ ├── main.m % 主程序:定义系统数据,调用建模与求解 ├── data/ │ └── case_data.m % 输入数据:负荷曲线、风光出力、设备参数、分时电价 ├── models/ │ ├── build_buses.m % 构建四条母线平衡约束 │ ├── build_mt.m % 燃气轮机模型 │ ├── build_el.m % 电解槽模型 │ ├── build_fc.m % 燃料电池模型 │ ├── build_h2tank.m % 储氢罐模型 │ └── build_storage.m % 蓄电池+蓄热罐模型 ├── solve/ │ └── solve_ies.m % 调用Yalmip和求解器 └── results/ ├── plot_results.m % 绘图:出力曲线、储能状态、成本构成 └── output_report.m % 输出关键指标:弃风弃光率、购能成本、氢产耗量main.m的核心任务是定义时间尺度(通常是 24 个时段)、从case_data.m中加载数据、调用各build_*函数构建约束,最后交给solve_ies.m求解并输出结果。这种“一个设备一个函数”的组织方式最大的好处是:增加新设备时不需要修改主程序内部的已有模块,只要新增一个build_xxx.m,在主函数里追加两行调用即可。
4.2 设备建模函数的关键写法
以电解槽为例,一个完整的Yalmip模型函数大概长这样:
function [cons, cost_expr, var] = build_el(data, T) % 变量定义 P_el = sdpvar(1, T); % 输入电功率 z_el = binvar(1, T); % 启停状态 H_el = sdpvar(1, T); % 产氢量 cons = []; % 出力范围 cons = [cons, P_el >= data.P_el_min .* z_el]; cons = [cons, P_el <= data.P_el_max .* z_el]; % 爬坡约束 for t = 2:T cons = [cons, -data.ramp_el <= P_el(t)-P_el(t-1) <= data.ramp_el]; end % 产氢量与耗电的分段线性关系(简化示例为线性) H_el = data.eta_el ./ data.LHV_H2 .* P_el; % 运维成本 cost_expr = sum(data.om_el .* P_el); var = struct('P_el',P_el, 'z_el',z_el, 'H_el',H_el); end注意这里用. *对矩阵做逐元素相乘,binvar定义0-1变量,Yalmip会自动将变量时间序列向量化处理。最关键的是返回参数里要把该设备涉及的变量集中打包到var结构体里,这样在main.m组装四条母线平衡约束时可以直接引用var.P_el等字段,省去到处找变量的麻烦。
4.3 求解流程与结果输出
求解部分的逻辑其实相当简单:
ops = sdpsettings('solver','gurobi','verbose',2, ... 'showprogress',1, 'gurobi.TimeLimit',300); result = optimize(cons_all, objective_all, ops);optimize函数返回后要立刻检查求解状态:
- 如果
result.problem == 0,说明求解成功; - 如果
result.problem == 1,说明模型不可行,直接去查约束集合里有没有冲突; - 如果
result.problem == 3,说明求解超时或数字困难,要么加时限,要么检查是否存在大范围数量级悬殊的系数。
结果输出方面,建议先输出三个核心指标:总运行成本、弃风弃光率、储氢罐全天总充放量。这三个指标能一眼判断出模型到底有没有把氢能系统“用起来”。如果调度结果里电解槽全天都是关机状态,那要么是氢价/电价激励不够,要么是建模约束把电解槽卡死了,需要回头检查数据。
4.4 一个简化算例的复现思路与初始参数
为了不涉及知识产权问题,这里给一个从公开文献典型数值整理的标准算例参数,读者可以拿来作为自己代码的自测基准。系统时间尺度取 24 小时,步长 1 小时,电负荷(kW)在 500~1200 之间波动,热负荷在 300~800 之间波动,风光出力曲线为典型的“光伏正午高、风电夜间高”形状。
| 设备 | 参数项 | 数值 |
|---|---|---|
| 燃气轮机 | 额定功率 | 400 kW |
| 燃气轮机 | 最小出力率 | 30% |
| 电解槽 | 额定功率 / 最小功率 | 300 kW / 90 kW |
| 电解槽 | 额定效率 | 70% |
| 燃料电池 | 额定功率 / 最小功率 | 200 kW / 60 kW |
| 燃料电池 | 发电效率 | 45% |
| 储氢罐 | 容量 | 200 kg |
| 储氢罐 | 初始氢量 | 100 kg |
| 蓄电池 | 容量 | 600 kWh |
| 分时电价 | 峰/平/谷 | 1.0 / 0.6 / 0.3 元/kWh |
| 购气价 | — | 2.5 元/m³ |
对于上面这套参数,一个合理的调度结果应该是:白天光伏大发的午间时段电解槽以较高功率运行,晚间燃料电池在负荷峰值时段放宽出力,储氢罐在日末回到初始 100kg 附近,弃风弃光率控制在 5% 以下。如果你跑出来的储氢罐趋势完全不同,大概率是成本参数激励方向错了。
5. 跑通算例后的实战总结:参数陷阱、收敛问题与结果分析
5.1 排查链路:从“不收敛”到“结果诡异”
MILP模型“求不出解”和“求出离谱解”是两种完全不同的头痛。先说不可行的情况,我建议按下面的链路排查,基本能解决九成问题:
- 第一步:检查所有储能设备的始末状态约束。这个最常见——某同学把储氢罐初始量设成 80kg,容量上限却只有 100kg,而又强行要求日末等于初始量,中间还要满足氢负荷,结果根本跑不出可行解。
- 第二步:检查四条母线平衡方程的符号。电平衡方程里,等式右边负荷项忘记加电锅炉和电解槽的功耗,就会导致模型“凭空多出一堆电能”,给出偏高的风电出力甚至无限购电的反常解。
- 第三步:检查爬坡约束的单位。如果出力单位是kW,而爬坡速率常数写成了MW级,那么约束会瞬间收紧到几乎无法调度,直接不可行。
如果求解器报告求解成功,但结果严重违反物理直觉,比如储氢罐一天充放了800kg而容量只有200kg,那多半是线性化环节出了问题——典型的是分段线性化时区间变量的约束写漏了,导致求解器在变量边界上“作弊”。
5.2 氢能设备参数的灵敏度问题
算例跑通之后,很多人会做参数敏感性分析,这里有一个特别容易误导人的点:氢能设备的参数与常规设备参数对总成本的影响尺度差异极大,但作用方向往往出乎意料。
例如,电解槽效率从70%降到60%,产氢量下降,储氢罐在日内的可用氢量变少,燃料电池夜间出力被迫减少,系统转而依赖更高成本的燃气轮机——总成本上升,这是符合直觉的。但如果你把电解槽额定功率从300kW提高到500kW,系统总成本未必下降,原因是电解槽的运维成本和启停成本随容量增大而增加,若光伏余电不够,高功率电解槽只能在低负载率下运行,效率掉得更厉害。
更反直觉的是:氢气售价(氢负荷卖给加氢站的收入)对调度结果影响极大,但总成本未必单调下降。原因是氢负荷售量增加时,电解槽需要多产氢,多消耗的电从哪里来?如果这个时段处于电价高峰期,那么“多卖氢”反而可能让购电成本暴涨,总成本不降反升。做灵敏度分析时,这样的“交叉影响”才是论文里的真正亮点,也值得在汇报时重点呈现。
5.3 对比设计的基准:是“有氢无氢”还是“不同氢价场景”
结果分析部分,我建议至少跑三组对照算例,而不是只跑一版主算例:
第一组是无氢能系统(把所有氢能相关设备的投建成本和启停变量直接移除,只保留燃气轮机、电锅炉、储电蓄热),作为基准方案。第二组是含氢能系统基础运行方案,采用上文第4.4节的全部设备,氢价取中等水平。第三组是高氢价或强碳排放约束方案,用于观察氢能在价格激励或碳约束下是否会被更加充分地利用。
这三组之间的差值,才是“计及氢能”四个字的价值所在。第一组和第二组的总成本差值体现了氢能设备引入后的净收益,第二组和第三组的差异则体现政策强度对氢能利用率的推动作用。在Matlab代码层面,只需要在main.m里用一个布尔开关控制是否调用build_el、build_fc、build_h2tank即可,十几分钟就能完成全套对比。
5.4 一个容易被忽略的边界条件:启停次数的隐性约束
最后提一个我在多份代码里反复看到的问题:很多模型只限制电解槽和燃料电池的最小运行功率,却不限制它们的启停次数。调度结果经常出现一天内电解槽启停五六次,每段只运行两三小时的情况。从MILP数学角度看,这是可行的;从电化学装置寿命角度看,这是灾难。
工程上通常的做法有两种。第一种是简单粗暴地给启动次数加上限,例如:
sum(z_start_el(t), t=1..T) ≤ N_max
需要额外引入启动变量 z_start_el(t),并用线性约束把“停机到运行的转换”表示出来。第二种做法是增加启动成本惩罚项,让求解器自己权衡启停带来的收益和代价。就我观察,第二种做法在代码实现上更简洁,也更容易在论文里解释“经济性驱动的运行策略”,所以更推荐优先使用。
6. 最后再分享两个实践中的小技巧
第一个是关于求解规模的把控。如果你的调度周期从 24 小时扩展到 168 小时(一周),设备启停变量和储能状态变量的数量会成倍增长,Gurobi 求解时间可能从十秒级暴涨到小时级。这时候别急着换电脑或换机器精度,先检查你的储能状态变量是不是被写成了多层嵌套索引数组,尽量把时间维压在最外层循环上,Yalmip 的稀疏表达效率会高很多。
第二个是关于结果绘图和汇报。氢能系统调度结果最好的可视化方式不是堆叠面积图,而是双轴图:左轴显示电母线供需曲线,右轴显示储氢罐存量的变化曲线。这样一眼就能看出“什么时候电解槽在充氢、什么时候燃料电池在放电、氢储能在这中间起到了多大的削峰填谷作用”。我见过很多报告全篇都是复杂的表格,反而不如一张这样的双轴图来得直观。
我在给某位同学调试项目代码时还注意过一个问题:氢负荷为零的时段如果系统仍然让电解槽满功率产氢,结果往往是因为储氢罐容量和初始氢量的设置不当导致“必须产氢来填容积”。小心这种约束驱动的伪调度行为,它会让你的论文结论失真。
最后一个建议,和代码水平无关,但对做研究很重要。跑完一版结果后,别急着改参数重跑,先花十分钟回到目标函数和四条母线方程,逐项核一下每一笔成本和每一股能流的单位统一了没有。氢能系统最大的隐患不是算法复杂,而是kW、kWh、kg、m³、元/吨这类单位在转化过程中悄悄错位。单位不乱,模型基本就稳了;单位一旦乱掉,所有灵敏度分析都会变成一场自欺欺人的数字游戏。