简介:这份MATLAB水文预报代码包面向水文水资源、水利工程方向的研究生与工程师,用于流域产流量计算与洪水过程线预测。包内共21个文件,以17个csv降雨与径流数据文件、3个m脚本和1个xls表格为主,压缩包约93KB,数据年份覆盖1989至2003年,便于直接替换自有数据开展实验。代码实现了日模型与次洪模型两条主线:日模型采用泰森多边形法估算流域面雨量,并利用蒸发资料率定作物系数Kc以刻画水分平衡;次洪模型则基于降雨与蒸发数据完成降雨径流转换,输出时间序列流量过程线。目前已有1313人学习下载,适合具备一定MATLAB基础、希望快速复现水文预报流程或进行参数调试的读者参考,也可作为教学与科研中的算法对照工具。
1. 从一场“数据对不上”的深夜调试说起
如果你做过水文预报,大概率经历过这种场景:辛辛苦苦整理好的降雨径流数据,跑进模型里出来的洪峰不是偏大就是偏小,参数调了一轮又一轮,最后发现是数据格式或者时间步长对不上。这套 MATLAB 版水文预报日模型和次洪模型代码,就是冲着这类问题来的。它把日尺度模拟和次洪(场次洪水)模拟两套流程都封装好了,数据放在 data.zip 里,解压就能跑。适合水文水资源方向的学生做课程设计、毕设,也适合刚入行的工程师快速验证算法思路。不用从零搭框架,改改参数就能看到结果,对新手相当友好。
2. 项目定位与技术选型:为什么是 MATLAB 而不是 Python
2.1 水文预报模型的两种尺度
水文预报按时间尺度大致分两类:日模型和次洪模型。日模型以天为步长,关注的是长期径流趋势、水量平衡,常用于流域水资源评估;次洪模型以小时或分钟为步长,关注的是单场降雨形成的洪水过程,对洪峰流量和峰现时间的模拟精度要求更高。这套代码把两者分开实现,说明作者对水文预报的业务场景是有理解的——日模型和次洪模型在产流机制、汇流计算、参数率定策略上都有明显差异,混在一起写反而容易乱。
从目录结构看,代码大概率是按“数据预处理 → 产流计算 → 汇流计算 → 参数率定 → 结果可视化”这条链路组织的。常见做法是每个模块一个函数文件,主脚本负责串联。这样做的好处是,你想替换某个产流算法,只需要改对应的函数,不用动整个流程。
2.2 MATLAB 在水文领域的现实优势
很多人会问,现在 Python 生态这么强,为什么还用 MATLAB?这个问题在水文圈其实有很实际的答案。第一,水文领域大量经典教材和论文的配套代码就是 MATLAB 写的,比如新安江模型、SCS 曲线数法、马斯京根法,网上能找到的参考实现大多是 .m 文件。第二,MATLAB 的矩阵运算语法对水文计算特别友好——降雨、蒸发、流量这些时间序列天然就是向量,一行代码就能做全序列运算,不用写循环。第三,很多水文单位的业务系统就是基于 MATLAB 搭建的,你拿这套代码去对接实际项目,迁移成本低。
当然 MATLAB 也有短板,比如部署麻烦、License 成本高。但如果你只是做算法验证和原型开发,这些都不是问题。这套代码的定位就是“能跑通、能改、能出图”,不是生产级部署方案。
2.3 data.zip 里可能有什么
虽然正文没给目录清单,但按水文预报模型的常规做法,data.zip 里大概率包含这几类文件:
- 降雨数据:通常是 .txt 或 .xlsx 格式,每列一个雨量站,每行一个时间步。
- 蒸发数据:日模型需要,次洪模型可能用不到。
- 流量数据:实测流量过程,用于参数率定和结果对比。
- 流域参数:面积、河长、坡度等,可能是 .mat 文件或写在脚本里。
- 主运行脚本:比如 run_daily.m 和 run_flood.m,分别对应日模型和次洪模型。
如果你解压后发现数据格式和预期不一致,别慌,先看主脚本里怎么读数据的,照着改就行。水文数据没有统一标准,每个项目都有自己的格式,这是常态。
3. 跑通第一个案例:从解压到出图的完整步骤
3.1 环境准备与数据检查
先把 MATLAB 装好,版本建议 R2018a 以上,太老的版本可能不支持某些函数。然后解压 data.zip,把文件夹添加到 MATLAB 的搜索路径里。常见做法是在主脚本开头加一行addpath(genpath(pwd)),这样所有子文件夹都能被找到。
数据检查这一步千万别跳过。我见过太多人直接跑脚本,结果报错说“矩阵维度不一致”,查了半天发现是数据里有个 NaN 或者某列少了一行。建议先写个小脚本把数据读进来,看看维度、范围、有没有缺失值。
% 数据检查脚本:读取降雨和流量数据,检查基本统计量 rain = readmatrix('data/rainfall.txt'); % 读取降雨数据 flow = readmatrix('data/streamflow.txt'); % 读取实测流量 fprintf('降雨数据维度: %d 行 %d 列\n', size(rain,1), size(rain,2)); fprintf('流量数据维度: %d 行 %d 列\n', size(flow,1), size(flow,2)); fprintf('降雨缺失值个数: %d\n', sum(isnan(rain(:)))); fprintf('流量缺失值个数: %d\n', sum(isnan(flow(:)))); % 检查时间步长是否一致 if size(rain,1) ~= size(flow,1) warning('降雨和流量时间步数不一致,请检查数据对齐'); end这段代码的逻辑很简单:读数据、看维度、查缺失。readmatrix是 R2019a 之后推荐的函数,老版本用xlsread或load也行。关键参数是文件路径,你得根据实际解压后的目录结构调整。如果降雨和流量行数不一致,说明时间步没对齐,后面跑模型肯定出错。
3.2 日模型运行流程
日模型的主脚本通常叫 run_daily.m 或类似名字。打开后你会看到几个关键步骤:读数据、设参数、算产流、算汇流、输出结果。参数一般集中在一个结构体里,方便修改。
% 日模型主脚本核心片段 % 步骤1:读取数据 data = load('data/daily_data.mat'); % 假设数据存为.mat文件 P = data.P; % 降雨,单位mm E = data.E; % 蒸发,单位mm Q_obs = data.Q; % 实测流量,单位m3/s % 步骤2:设置模型参数 params = struct(); params.area = 500; % 流域面积,km2 params.K = 0.8; % 汇流参数,反映流域调蓄能力 params.B = 0.3; % 产流参数,反映下垫面特性 params.Imax = 20; % 最大下渗量,mm % 步骤3:运行模型 [Q_sim, storage] = daily_model(P, E, params); % 步骤4:结果评估 NSE = 1 - sum((Q_obs - Q_sim).^2) / sum((Q_obs - mean(Q_obs)).^2); fprintf('纳什效率系数 NSE = %.3f\n', NSE); % 步骤5:绘图对比 figure; plot(Q_obs, 'b-', 'LineWidth', 1.5); hold on; plot(Q_sim, 'r--', 'LineWidth', 1.5); xlabel('时间步(天)'); ylabel('流量(m3/s)'); legend('实测', '模拟'); title('日模型流量模拟结果');这段代码展示了日模型的核心流程。daily_model是封装好的函数,输入降雨、蒸发和参数,输出模拟流量和蓄水量。参数里K和B是最需要调的,K越大汇流越慢,B影响产流曲线的形状。NSE 是水文模型最常用的评价指标,大于 0.7 算合格,大于 0.8 算不错。如果 NSE 是负数,说明模型还不如直接用实测均值,这时候得回头检查参数或者数据。
3.3 次洪模型运行流程
次洪模型的主脚本类似,但时间步长更细,通常是小时级别。次洪模型对降雨的时空分布更敏感,所以数据预处理阶段可能需要做插值或者时段转换。
% 次洪模型主脚本核心片段 % 读取场次洪水数据 flood_data = load('data/flood_event.mat'); P_flood = flood_data.P; % 小时降雨,mm/h Q_flood = flood_data.Q; % 小时流量,m3/s % 次洪模型参数 flood_params = struct(); flood_params.area = 500; flood_params.n = 0.6; % 曼宁糙率系数 flood_params.L = 50; % 河道长度,km flood_params.S = 0.001; % 河道坡度 % 运行次洪模型 Q_sim_flood = flood_model(P_flood, flood_params); % 洪峰误差计算 peak_obs = max(Q_flood); peak_sim = max(Q_sim_flood); peak_error = (peak_sim - peak_obs) / peak_obs * 100; fprintf('洪峰相对误差: %.2f%%\n', peak_error);次洪模型的参数和日模型不一样,n、L、S这些是水力学方法的参数。洪峰误差是次洪模型的核心指标,一般要求控制在 20% 以内。如果误差太大,先检查降雨输入的单位是不是 mm/h,再检查河道坡度是不是量级搞错了——这两个地方最容易翻车。
4. 核心模块拆解:产流、汇流与参数率定
4.1 产流计算模块
产流是水文模型的第一步,决定有多少降雨变成径流。这套代码大概率用的是蓄满产流或者超渗产流。蓄满产流适合湿润地区,超渗产流适合干旱地区。具体用哪种,看代码里的函数名和注释。
function [R, storage] = runoff_generation(P, E, Imax, storage_prev) % 蓄满产流计算 % 输入:P-降雨,E-蒸发,Imax-最大蓄水量,storage_prev-前期蓄水量 % 输出:R-产流量,storage-时段末蓄水量 storage = storage_prev + P - E; % 水量平衡 R = zeros(size(P)); % 初始化产流 for t = 1:length(P) if storage(t) > Imax R(t) = storage(t) - Imax; % 超过蓄水容量的部分产流 storage(t) = Imax; % 蓄水量不超过最大值 end end end这段代码是典型的蓄满产流实现。核心逻辑是:降雨扣除蒸发后存入土壤,超过最大蓄水容量Imax的部分变成径流。Imax是关键参数,越大说明流域越“能装”,产流越少。实际应用中,Imax通常按流域土壤类型和土地利用情况来定,没有实测数据的话可以参考类似流域的经验值。
4.2 汇流计算模块
汇流是把产流产生的径流沿坡面和河道汇集到出口断面的过程。常见方法有单位线法、马斯京根法、线性水库法。这套代码里大概率用的是线性水库或者马斯京根。
function Q = flow_routing(R, K, area, dt) % 线性水库汇流计算 % 输入:R-产流序列,K-汇流参数,area-流域面积,dt-时间步长 % 输出:Q-出口断面流量 % 单位转换:mm*km2 -> m3/s conv_factor = area * 1e6 / (dt * 3600) / 1000; Q = zeros(size(R)); storage = 0; for t = 1:length(R) inflow = R(t) * conv_factor; % 入流 Q(t) = storage / K; % 出流与蓄量成正比 storage = storage + (inflow - Q(t)) * dt; % 蓄量更新 end end线性水库的核心公式是Q = S/K,S是蓄量,K是蓄量常数。K越大,退水越慢,洪峰越平缓。conv_factor那行是单位换算,把毫米乘以平方公里转换成立方米每秒,这个换算很容易搞错,建议单独写个函数测试一下。如果模拟的洪峰明显偏小,先检查这个换算系数。
4.3 参数率定模块
参数率定是水文模型最耗时的环节。这套代码可能提供了手动调参和自动优化两种方式。自动优化常见做法是用 SCE-UA 算法或者遗传算法。
% 简单的网格搜索参数率定示例 K_range = 0.5:0.1:2.0; % 汇流参数范围 B_range = 0.1:0.1:1.0; % 产流参数范围 best_NSE = -inf; best_params = [0, 0]; for K = K_range for B = B_range params.K = K; params.B = B; Q_sim = daily_model(P, E, params); NSE = 1 - sum((Q_obs - Q_sim).^2) / sum((Q_obs - mean(Q_obs)).^2); if NSE > best_NSE best_NSE = NSE; best_params = [K, B]; end end end fprintf('最优参数: K=%.2f, B=%.2f, NSE=%.3f\n', best_params(1), best_params(2), best_NSE);网格搜索虽然笨,但对新手最友好,不容易陷入局部最优。缺点是计算量大,参数多的时候跑得慢。实际项目中,我一般先用网格搜索粗调,再用fminsearch或者lsqnonlin精调。注意daily_model每次调用都会重新读数据的话会很慢,建议把数据读一次存成全局变量或者用匿名函数传进去。
5. 避坑与常见问题排查
5.1 数据维度不一致报错
现象:运行脚本时报“矩阵维度必须一致”或者“索引超出矩阵维度”。
原因:降雨和流量数据的时间步数不一样,或者某个数据文件里有多余的表头行。水文数据来源杂,有的从 Excel 复制出来带表头,有的从数据库导出没有,混在一起就容易出问题。
解决:跑模型前先单独读数据,用size看维度,用isnan查缺失。如果行数差一行,大概率是表头问题,用readmatrix的'NumHeaderLines'参数跳过。
5.2 模拟流量全是零或者全是 NaN
现象:模型跑完了,但Q_sim全是 0 或者 NaN。
原因:产流模块里Imax设得太大,所有降雨都被土壤吸走了,没有径流产生。或者单位换算搞错了,比如把毫米当成米,结果数值小到可以忽略。
解决:先把Imax调小到 5 以下试试,如果还是零,检查产流函数的输入单位。常见做法是在函数入口加一句assert(max(P) > 1, '降雨数据可能单位不对'),提前拦截。
5.3 NSE 为负数
现象:模型能跑通,但 NSE 是负的,模拟流量和实测流量完全对不上。
原因:参数严重偏离合理范围,或者汇流参数K的时间单位和模型时间步长不匹配。比如K是按小时算的,但日模型的时间步是 1 天,直接套用就会差 24 倍。
解决:先检查K的单位。日模型的K一般在 0.5 到 5 之间,次洪模型的K在 0.1 到 2 之间。如果量级差太多,说明单位没统一。另外,把实测和模拟流量画在一起,看看是整体偏大还是偏小,还是相位对不上,不同情况调不同参数。
5.4 次洪模型洪峰时间对不上
现象:洪峰大小差不多,但模拟的洪峰比实测早到或者晚到好几个小时。
原因:汇流速度参数不对。次洪模型里河道长度L和坡度S决定汇流时间,如果L填的是公里但公式里要米,或者S填的是百分数但公式里要小数,都会导致汇流时间算错。
解决:把L和S的单位在脚本注释里写清楚,每次改参数前先看一眼。我一般会在参数结构体旁边加一行注释,比如% L单位: km, S单位: m/m,省得后面忘了。
5.5 自动率定跑得太慢
现象:用网格搜索调参,跑了一个小时还没出结果。
原因:每次调用模型函数都重新读数据,或者循环层数太多。
解决:把数据读一次存成persistent变量或者用containers.Map缓存。另外,先用粗网格定位大致范围,再用细网格精调。如果参数超过 3 个,网格搜索就不合适了,换 SCE-UA 或者粒子群算法。
6. 二次开发与改造思路
拿到这套代码后,最直接的改造方向是替换产流或汇流算法。比如你手头的流域是岩溶地区,蓄满产流不合适,想换成 SCS 曲线数法。操作步骤很简单:找到runoff_generation.m,把里面的逻辑替换成 SCS 的公式,保持输入输出接口不变,主脚本不用动。SCS 的核心是Q = (P - 0.2S)^2 / (P + 0.8S),其中S是径流曲线数CN换算出来的,CN可以根据土地利用和土壤类型查表。
第二个改造方向是加一个灵敏度分析模块。水文模型参数多,不是每个都重要。用 Morris 筛选法或者 Sobol 指数跑一遍,能看出哪些参数对结果影响大,调参的时候就有重点了。MATLAB 里可以用sensitivity包,或者自己写个简单的扰动测试:每次只动一个参数,看 NSE 变化多少。
第三个方向是接实时预报。把模型封装成函数,输入最近的降雨预报和实测流量,输出未来几天的流量过程。这个需要处理数据滚动更新和状态变量传递,稍微复杂点,但思路是一样的——把storage作为状态变量存下来,下次调用时传进去。
验证改造是否成功,最直接的方法是对比 NSE 和洪峰误差。如果新算法的 NSE 比原来高 0.05 以上,或者洪峰误差降低 5 个百分点,说明改造有效。但别只看这两个指标,还要看径流总量误差和退水段拟合情况,有时候 NSE 高了但退水段反而变差了。
从那以后我每次改模型代码,都强制走一遍“单场洪水验证 → 连续日模型验证 → 参数灵敏度检查”这三步,少一步都不敢往业务系统里放。希望这套代码能帮你省掉从零搭框架的时间,把精力花在算法改进上。
本文还有配套的精品资源,点击获取