简介:《MATLAB在水文计算中的应用》是一份面向水文、水利专业学生及工程技术人员的参考文献。内容围绕单位线推求、相关分析、系列插补延长等典型水文计算任务,讲解如何借助MATLAB矩阵运算与最小二乘法完成求解,相比传统手算方法更快捷、准确,适合作为课程学习与项目计算的辅助资料。资源包共1个文件,格式为PDF,整体大小约153KB,属于期刊论文排版,内含理论推导、公式示例与计算实例表格,可直接阅读或打印使用。目前已有270人学习浏览。借助这篇文献,读者可掌握用MATLAB处理水文频率计算、汇流曲线推求的基本思路,并了解从实测降雨径流资料建立矩阵方程、求解单位线的完整流程,对开展水文计算或MATLAB数值分析有一定参考价值,也适用于课程设计、毕业设计等场景。
1. 拿到三十年的日降雨和日流量,用 MATLAB 把整条水文计算链路收进脚本
水文计算的典型场景是资料整编和设计洪水推求:手里有三十年的日降雨、日流量、水位过程,需要按时段摘录、插补缺测、按年最大值取样,再做 P-III 型频率分析推求百年一遇设计值,最后用单位线法或马斯京根法做一次洪水演算。这套流程在 Excel 里能跑,但每换一个站就要重来一遍:列错位、日期格式不统一、插值方式张冠李戴,半夜容易在某个筛选公式上翻车。MATLAB 在水文计算中的优势不是某个函数有多强,而是把读数、清洗、矩估计、适线、绘图、演算全部放进同一个脚本环境,输入换一个文件,结果整套重算,中间每一个环节的参数都可审查、可复现。这篇文章按我处理水文资料的实际顺序来写,从数据读入到频率分析再到洪水演算,每一步都给出可运行的代码和参数边界,适合刚接手水文数据处理的人照着搭一套自己的脚本,也适合老手对照检查自己在插值、适线和参数率定上有没有偷懒。
2. 用 MATLAB 读取水文资料:日期解析、缺测插值与按年取样
2.1 用 readtable 和 detectImportOptions 读入日降雨、日流量,处理两种日期格式
水文站的原始资料最常见的是 Excel 表格,列结构一般是站名、日期、日雨量、日平均流量,偶尔混着水位、蒸发。直接用readtable读 Excel 没问题,但日期列经常被 MATLAB 自动解析成 datetime 序列值,或者因混入文本而整列变成 cell,后面计算直接报错。我一般的处理方式是先让detectImportOptions探一遍列类型,再手动锁定日期和数值列。
% 指定 Excel 文件路径 file = 'rain_daily.xlsx'; opts = detectImportOptions(file); % 锁定列类型:第2列日期,第3列雨量,第4列流量 opts.VariableTypes = {'char', 'datetime', 'double', 'double'}; opts.VariableNames = {'station', 'date', 'rain', 'flow'}; data = readtable(file, opts); data.date = datetime(data.date, 'InputFormat', 'yyyy-MM-dd'); % 统一格式这段代码里最关键的是VariableTypes和VariableNames一一对应,顺序错一位就会把雨量读成流量。datetime类型的转换格式InputFormat要和 Excel 里的实际显示格式匹配,有的站导出的是yyyy/MM/dd,有的是yyyy-MM-dd HH:mm:ss,统一成yyyy-MM-dd后后续retime才不会有歧义。
读入后先看一眼summary(data),确认每列非 NaN 数量。水文站资料里常见的坑是 2 月 29 日在非闰年出现,datetime直接报错,这时需要把原始日期先读成char,再用datetime的'Format'选项配合'InputFormat'做容错,或者干脆在 Excel 侧先过滤脏行。另一个坑是流量为负值,出现在退水段回水顶托的测点,这些点不能直接当异常删,要结合水位过程判断。
如果手里是 CSV 文件,读法一样,只是detectImportOptions改为自动识别逗号分隔,readtable可以直接处理。遇到上百兆的长序列日资料,readtable速度尚可,但后续清洗建议用timetable结构,内存使用更紧凑,时间索引操作也顺手得多。
2.2 缺测插补与异常值清洗:fillmissing、isoutlier 和水文场景的取舍
实测水文序列缺测几乎是常态,雨量站漏测、流量站检修断测都很常见。MATLAB 里fillmissing提供了多种插补方式,但水文数据不能无脑选linear。日流量在退水段天然是指数衰减趋势,linear插值会在两个实测点之间拉出一条直线,使得退水过程变形,影响后续单位线推求;日降雨则完全不同,雨量是离散事件,在内陆站连续缺测三天,linear会把一段不存在的雨“造”出来。
% 流量缺测用 PCHIP 插值,保持退水段的单调形状 flow_filled = fillmissing(data.flow, 'pchip'); % 雨量缺测用 previous 填充,代表无雨日 rain_filled = fillmissing(data.rain, 'previous'); % 超过窗口内 5 倍 MAD 的流量点标记为异常,置为 NaN 后重新插值 bad = isoutlier(flow_filled, 'movmedian', 7, 'ThresholdFactor', 5); flow_filled(bad) = NaN; flow_filled = fillmissing(flow_filled, 'linear');fillmissing的'pchip'保形插值在退水段比linear平滑,比'spline'更不容易出现过冲负值,这是我在流量序列上优先选它的原因。isoutlier的'movmedian'方法用滑动中位数作为基准,比均值更抗局部脉冲干扰,ThresholdFactor默认是 3,水文流量在洪水期本身波动大,我一般调到 5,只剔那些明显偏离周边过程的孤立点。bad索引置为 NaN 再插值,相当于把异常点当作缺测处理。
| 插值方法 | 适用场景 | 水文使用建议 |
|---|---|---|
linear | 短时段、变化平缓 | 不推荐用于退水段,会拉平峰值 |
pchip | 保持单调和形状 | 日流量、水位插补首选 |
spline | 光滑曲线、趋势分析 | 可能产生负值,慎用于流量 |
previous | 离散事件、状态保持 | 雨量缺测常用 |
movmean/movmedian | 滑动窗口平滑 | 配合isoutlier做异常检测 |
清洗逻辑里最容易犯的错是先用fillmissing补完再做isoutlier,这样异常点已经被插补值掩盖,根本检测不出来。正确顺序是先标记异常、置 NaN,再统一插补。另外,连续缺测超过 10 天的流量段,我不建议插补,直接把整段标记为无效,后续频率分析取样时跳过该年,否则人为构造的连续退水段会污染极值样本。
2.3 用 timetable 和 retime 按年最大值取样,生成频率分析输入序列
频率分析需要的是独立样本,水文上普遍采用年最大值法:每年只取一个最大日流量(或最大时段降雨),这样样本量等于资料年数,且各样本间基本独立。readtable读进来的数据先转成timetable,再按年度聚合,比手动find每年最大值的做法简洁得多。
% 构造时间表 tt = timetable(data.date, rain_filled, flow_filled, ... 'VariableNames', {'rain', 'flow'}); % 按年聚合,取每年最大日流量 annual_flow = retime(tt, 'yearly', 'max'); % 只保留完整年份(1月1日到12月31日都有数据) annual_flow.Properties.RowTimes = dateshift(annual_flow.Properties.RowTimes, 'start', 'year');retime的'yearly'选项把时间轴按年分组,'max'对每个年份窗口取最大值,这是水文频率分析里最常用的取样方式。dateshift把时间标签对齐到每年年初,方便后续与实测资料年份比对。这里有个细节:如果某年缺测超过 30 天,retime仍然会给出该年的最大值,但这个最大值不可信。所以我一般在retime之前先统计每年有效观测天数,少于 330 天的年份直接置为 NaN,后续频率分析里自动忽略。年降雨量如果也要分析,同样用retime(tt, 'yearly', 'sum')聚合,注意这是求和不是取最大。
取样完成后,把annual_flow导出成纯数值数组,频率分析脚本就可以完全不依赖时间信息了。
3. 在 MATLAB 里实现 P-III 型频率分析:矩估计、离均系数与自动适线
3.1 P-III 型分布的三个参数在水文设计里的意义
我国水文频率计算规范推荐的设计洪水线型是皮尔逊 III 型分布(P-III),它本质上是一个带偏态的三参数伽马分布族。三个参数分别是均值、变差系数 Cv 和偏态系数 Cs,三者共同决定频率曲线的形状:均值决定曲线整体高低,Cv 决定曲线离散程度,Cv 越大设计值随频率变化越陡;Cs 决定曲线的偏态程度,Cs 大于 0 时曲线在高频段翘起,反映水文极值“大值更极端”的分布特征。
实际资料中,Cv 和 Cs 不是独立估计的,规范里通常有“Cs 取 Cv 的倍数”这一经验做法,湿润地区暴雨和洪水一般取Cs = 2~4 * Cv,干旱地区 Cv 本身偏大,Cs/Cv 可以取到 3~6。直接由样本矩估计的 Cs 波动极大,尤其样本量少于 30 年时,三阶矩对个别极值极其敏感,所以工程上常把 Cs 当作适线参数来调和,而不是直接信任矩估计值。
P-III 型分布没有解析的频率曲线表达式,传统做法是查《水文频率计算手册》里的离均系数表,给定频率 p、偏态系数 Cs,查出离均系数 Φ,再按公式计算设计值:
x_p = mean * (1 + Cv * Φ)MATLAB 里不需要查表,可以用 Wilson-Hilferty 近似公式直接计算离均系数,误差在工程允许范围内,也可以在已知均值和 Cv 的前提下用gaminv精确求解分位数。我习惯先用 WH 近似做初值,再在适线阶段用优化去微调 Cs,这样速度和精度兼顾。
3.2 矩估计计算 Cv 和 Cs,用 Wilson-Hilferty 近似求离均系数
设年最大流量序列为x,长度为n。矩估计公式为:均值等于样本均值,Cv 等于样本标准差除以均值,Cs 的矩估计用三阶中心矩除以标准差的三次方,并乘一个无偏修正因子。直接代入 WH 近似公式计算各频率对应的离均系数。
x = annual_flow.flow(~isnan(annual_flow.flow)); % 剔除无效年份 n = length(x); mean_x = mean(x); std_x = std(x); cv = std_x / mean_x; cs = (n / ((n-1)*(n-2))) * sum(((x - mean_x) / std_x).^3); % 无偏矩估计 % 频率点(%),从 0.01% 到 99.9%,覆盖设计洪水关注的尾部 p = [0.01, 0.05, 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 75, 90, 95, 99, 99.9]; % 标准正态离均系数 xi = norminv(1 - p/100); % Wilson-Hilferty 近似计算 P-III 离均系数 if abs(cs) < 1e-6 phi = xi; % Cs=0 退化为正态分布 else phi = (2/cs) * (1 + cs*xi/6 - cs^2/36).^3 - 2/cs; end % 设计值 xp = mean_x * (1 + cv * phi);norminv求的是标准正态分布左侧分位数,1 - p/100把频率 p 转为超过概率的补数。WH 公式里cs作为分母,当样本偏态系数接近 0 时必须走if分支,否则数值不稳定。计算出的xp就是对应重现期的设计值,比如p=1对应百年一遇。这里有个细节:若cs为负值,WH 近似精度下降,但这在水文极值序列里极少出现,一旦遇到先检查样本是不是混入了非洪水年份的枯季流量。
我通常把这段代码封装成一个函数p3quantile(mean_x, cv, cs, p),后面自动适线时要反复调用上千次,函数化之后可以避免把公式复制得到处都是。
3.3 用 fminsearch 做自动适线:目标函数、参数边界和初值设置
矩估计的 Cv 和 Cs 只是初值,规范方法还要通过适线来调整:在频率格纸上点绘经验频率点据,调整统计参数使理论频率曲线尽量贴近点据。目估适线主观性太强,换成脚本后可以用最小二乘自动完成,目标函数取经验频率点据对应流量与理论曲线流量的残差平方和。这个优化问题只有两个自由度,MATLAB 优化工具箱里的fminsearch就够用。
% 经验频率:Weibull 公式 pm = (1:n)' / (n+1); xs = sort(x, 'descend'); % 目标函数:固定均值,调整 Cv、Cs fun = @(params) sum((xs - p3quantile(mean_x, params(1), params(2), pm*100)).^2); % 初值:Cv 用矩估计,Cs 取 2 倍 Cv params0 = [cv, 2*cv]; % 边界约束用对数变换实现 obj = @(q) fun([exp(q(1)), exp(q(2))]); best = fminsearch(obj, log(params0)); cv_fit = exp(best(1)); cs_fit = exp(best(2));目标函数里比较的是同频率下的流量值,xs是从大到小排列的经验点据,pm是对应的经验频率,两者一一对应。直接用fminsearch容易撞边界,因为 Cs 太小或太大都会让目标函数呈现平台区,我用log参数化把 Cv、Cs 约束到正数域,既避免负参数,又让优化在数量级上更稳定。拟合结果要回代画图,如果理论曲线在特大洪水那一段偏离点据明显,先检查经验频率公式是否用了Weibull,部分规范里也可以改用 Gringorten 公式(m-0.44)/(n+0.12),两者对最大值的频率估计差异在小样本时不可忽略。
fminsearch是 Nelder-Mead 单纯形法,不依赖梯度,对这类二维光滑问题足够。若还想再压制 Cs 过度调整,可以在目标函数里加一个惩罚项,把Cs/Cv的比值拉回规范经验区间 2~4,我用过的最简单形式是给目标函数加上lambda * (cs/cv - 3)^2,lambda取 0.01 量级即可。
3.4 频率格纸坐标变换:把概率轴映射到正态分位数,绘出 P-III 频率曲线
频率曲线的横轴是概率,但印刷的“频率格纸”并非等距刻度,而是按正态分布离均系数压缩过,目的是让正态分布的频率曲线在图上呈直线。MATLAB 默认坐标轴不支持这种概率刻度,手动做法是设置XTick为频率对应的正态分位数位置,再改写刻度标签。
% 理论频率曲线 p_theory = logspace(-3, 0, 100); % 0.1% 到 100% xp_theory = p3quantile(mean_x, cv_fit, cs_fit, p_theory*100); % 绘图 figure; plot(norminv(1 - pm), xs, 'o', 'MarkerSize', 6); hold on; plot(norminv(1 - p_theory), xp_theory, '-', 'LineWidth', 1.5); grid on; % 设置概率轴刻度 xtick_p = [0.1, 0.5, 1, 2, 5, 10, 20, 50, 75, 90, 95, 99, 99.9]; xticks(norminv(1 - xtick_p/100)); xticklabels(string(xtick_p)); xlabel('频率 P (%)'); ylabel('流量 (m^3/s)');logspace生成对数均匀的频率序列,让曲线尾部平滑;norminv把频率从概率空间映射到正态分位数空间,作为绘图横坐标。这样点据和理论曲线在横轴上都对齐了,如果再配合semilogy做纵轴对数变换,整条曲线在低频率段的形状变化看得更清楚。水文规范要求频率曲线在 0.01% 到 99.9% 范围内绘制,坐标变换后高频段(50% 以上)会被压缩得很窄,点据挤在一起,这是正常现象,不需要强行调整坐标轴范围。
绘图完成后,把拟合参数、设计值列表、频率曲线图一起导出,一份频率分析报告的关键输出就算齐了。
4. 用 MATLAB 推求单位线与马斯京根法洪水演算
4.1 最小二乘反卷积推求单位线:矩阵形式、非负约束与病态处理
单位线法把流域看成线性时不变系统:净雨过程经过流域汇流得到出口断面流量过程,数学上就是卷积。已知净雨序列P和流量过程Q,推求单位线U是一个反卷积问题,离散形式可以写成线性方程组。用最小二乘求解,同时施加非负约束。
% 净雨过程 P(m 个时段),流量过程 Q(n 个时段) P = [10; 25; 15]; % 单位 mm,三个时段净雨 Q = [5; 30; 60; 40; 20]; % 实测流量过程,单位 m3/s % 构造卷积矩阵:每一列是净雨序列平移一个时段 m = length(P); n = length(Q); A = zeros(n, m); for j = 1:m A(j:end, j) = P(1:n-j+1); end % 非负最小二乘求解单位线 U = lsqnonneg(A, Q); % 计算还原流量,验证拟合效果 Q_hat = A * U;A的每一列对应净雨序列在不同时段的贡献,第 j 列的起始行是第 j 个时段,这是单位线推求的标准矩阵构造法。lsqnonneg来自优化工具箱,强制单位线元素非负,这一点很重要——直接A\Q得到的解经常出现负值,物理上说不通,因为负的单位线意味着降雨导致流量减少。单位线推求对净雨过程分割极其敏感,净雨时段越长,A矩阵病态越严重,lsqnonneg也未必能给出稳定解。遇到这种情况,我一般先对Q做基流分割,把地面径流和基流分开,只用地表径流过程参与反卷积,否则推出来的单位线会带一个很长的虚假退水尾巴。
验证环节看max(abs(Q - Q_hat)),如果误差集中在洪峰附近,多半是净雨分割误差,而不是单位线本身的问题。
4.2 马斯京根法差分格式:C0/C1/C2 的计算公式与参数取值范围
马斯京根法是河道洪水演算的标准方法,把河段蓄量表示为入流和出流的加权组合,再结合水量平衡方程离散成差分格式。每个时段末的出流Q2由本时段入流I2、上一时段入流I1和上一时段出流Q1线性组合得到。参数K表示洪水波在河段内的传播时间,X是流量比重因子,反映河段的楔蓄特性。
| 参数 | 含义 | 一般取值范围 |
|---|---|---|
K | 洪水波传播时间 | 河段长度的函数,数小时到数十小时 |
X | 楔蓄因子 | 天然河道 0.1~0.3,渠化河道 0.3~0.4 |
dt | 演算时段长 | 取K的 1/2 到 1/3,满足稳定条件 |
三个系数的计算公式为:
C0 = (dt - 2*K*X) / (2*K*(1-X) + dt) C1 = (dt + 2*K*X) / (2*K*(1-X) + dt) C2 = (2*K*(1-X) - dt) / (2*K*(1-X) + dt)系数和恒等于 1,这是马斯京根法的守恒条件。MATLAB 实现时写成函数,方便后续率定环节反复调用。
function Q = muskingum(I, Q1, K, X, dt) % I: 入流过程向量; Q1: 初始出流; K, X: 演算参数 % dt: 时段长, 单位与 K 一致 C0 = (dt - 2*K*X) / (2*K*(1-X) + dt); C1 = (dt + 2*K*X) / (2*K*(1-X) + dt); C2 = (2*K*(1-X) - dt) / (2*K*(1-X) + dt); n = length(I); Q = zeros(n, 1); Q(1) = Q1; for t = 2:n Q(t) = C0*I(t) + C1*I(t-1) + C2*Q(t-1); end endC2为负并不罕见,这取决于K、X、dt的相对大小。C2过负会导致演算出的流量过程振荡,工程上要求dt > 2*K*X来保证系数合理。实际率定时我会在目标函数里加一个检查项,如果C2 < -0.5直接返回一个很大的误差值,避免优化器把参数跑到无物理意义的区域。
4.3 用 fminsearch 率定马斯京根参数,以 Nash-Sutcliffe 效率为目标
马斯京根参数率和单位线推求不同,不需要人工试错。实测河段的上游入流I和下游出流Q_obs都有,只要给定一组K、X,就能演算出一组Q_sim,然后通过目标函数量化两者的差距。目标函数最常用的是 Nash-Sutcliffe 效率 NSE,计算式如下:
NSE = 1 - sum((Q_obs - Q_sim).^2) / sum((Q_obs - mean(Q_obs)).^2)NSE 越接近 1,模拟效果越好。NSE 对洪峰误差敏感,如果希望洪峰附近权重更高,可以对残差施加指数权重,把目标函数写成加权形式。率定代码直接调用fminsearch,参数初值按河段水力特征估计:我先用洪峰传播时间估算K的初值,X从 0.2 起步。
% 实测数据:上游入流和下游出流 I = inflow; Q_obs = outflow; dt = 6; % 小时 % 目标函数:最小化 1-NSE fun = @(theta) 1 - nse(muskingum(I, Q_obs(1), theta(1), theta(2), dt), Q_obs); % 参数变换:K > 0,X 约束在 [0, 0.5] obj = @(q) fun([exp(q(1)), 0.5 * (1 - exp(-q(2)))]); best = fminsearch(obj, [log(12), log(1)]); K_opt = exp(best(1)); X_opt = 0.5 * (1 - exp(-best(2))); % 最终演算与拟合图 Q_sim = muskingum(I, Q_obs(1), K_opt, X_opt, dt); plot(Q_obs, 'o'); hold on; plot(Q_sim, '-');theta(1)用exp保证K为正,theta(2)用逻辑斯蒂变换把X压到[0, 0.5]区间内,这是率定约束边界的常用小技巧,比fminsearch支持边界约束更可靠。初值里log(12)对应 K 初值 12 小时,log(1)对应 X 初值约 0.32,整个优化通常 50 步以内收敛,因为目标函数在这个二维参数空间里相对光滑。
率定完成后检查三个系数是否满足稳定性:dt > 2*K*X必须成立,否则演算过程会出现锯齿状振荡。水利工程做预报方案时,K值还会按流量级分档率定,因为天然河道的传播时间随流量增大而减小,分档率定后预报精度能明显提升。若后续要接入数据驱动模型做更长期的预报,常见的bp神经网络拟合曲线方法也能在同一套 MATLAB 环境里跑,只需要用mapminmax把流量序列归一化到[-1,1]区间再训练,效果比直接用原始量级数据稳定得多。
5. 把水文计算封装成可复用函数:输入输出设计、交互式率定与验证指标
5.1 把频率分析和洪水演算固化成函数文件,定义清晰的输入输出接口
脚本写完之后,下一步是做封装。水文计算的特点是同一套方法要反复套用在多个站点、多个时段资料上,每次复制粘贴脚本改文件名既不安全也不高效。我一般把流程拆成三个独立函数:readHydroData负责读数和清洗,p3fitAndPlot负责频率分析,muskingumCalibrate负责洪水演算率定。函数接口如下:
function [params, design_value] = p3fit(x, p) % x: 年极值流量序列 % p: 需要输出的设计频率,如 [0.1, 1, 2] 对应千年/百年/五十年 % params: 结构体,含 mean, cv, cs % design_value: 与 p 对应的设计流量 end function [K, X, nse_val] = muskingumCalibrate(I, Q, dt) % I: 上游入流过程; Q: 下游实测出流过程; dt: 时段长 % K, X: 率定后的马斯京根参数; nse_val: 最终效率系数 end封装的重点不在函数内部逻辑,而在输入校验。p3fit的x里如果混入 NaN,计算均值时直接出错,函数入口先x = x(~isnan(x))并警告用户有效样本数少于 20 年;muskingumCalibrate的I和Q长度不一致,卷积矩阵构造直接报错,入口处统一截短到相同长度。这些校验占不了几行代码,但能避免下游调用时排查半天找不到原因。
5.2 用实时脚本的数值滑块让 K、X 参数率定过程可视化
很多单位没有专门的率定软件,交付成果时需要展示参数调整过程。MATLAB Live Editor 里可以插入数值滑块控件,省去自己写 GUI 的功夫。把马斯京根演算函数放进实时脚本,K和X各放一个滑块,改动滑块时整个流量过程图联动重绘,既适合自己快速找初值,也适合在评审时演示参数敏感性。以下代码放到实时脚本的代码块中:
K = 12; % 改成滑块的绑定变量 X = 0.25; Q_sim = muskingum(I, Q_obs(1), K, X, dt); plot(Q_obs, 'o'); hold on; plot(Q_sim, '-'); hold off; legend('实测', '演算');滑块控件绑定的变量范围在 Live Editor 右侧面板里设置,K设[2, 48]步长 0.5,X设[0, 0.5]步长 0.01。手动拖动滑块观察曲线贴合程度,顺便检验fminsearch找出的最优值是不是落在肉眼可见的合理区间内。这个交叉验证很值得做,因为优化目标函数 NSE 对退水段拟合好、洪峰拟合差的解,评分仍然可能很高,人眼一扫就能发现洪峰对不上,直接回退参数初始估计。
5.3 多组初始点交叉验证与误差指标输出
fminsearch是局部优化算法,最终结果依赖初值。水文参数空间虽然光滑,但不能排除多个局部极小点。我常用的做法是从三个不同的(K, X)初值出发分别率定,比较收敛后的目标函数值和参数值;如果三组结果 NSE 差异小于 0.005,认为率定结果可信;如果差异大,取效果最好的一组,同时列出每组的参数供人工判断。验证时除 NSE 外,还要输出洪峰相对误差和峰现时间误差两项指标:
err_peak = (max(Q_sim) - max(Q_obs)) / max(Q_obs) * 100; t_obs = find(Q_obs == max(Q_obs)); t_sim = find(Q_sim == max(Q_sim)); err_time = t_sim - t_obs;这三项指标组合起来才是一个完整的评价体系:NSE 反映整体过程拟合程度,洪峰相对误差控制防洪安全余量,峰现时间误差检验K参数是否合理。频率分析部分也可以用类似方法验证——固定 Cv、扫描 Cs 的值画一条目标函数曲线,看最优点附近是否平缓,并用经验频率点据与理论曲线的最大相对误差辅助判断适配度。把这三个输出固定到函数返回值里,后续批量处理多个站点、给每个站生成参数汇总表的时候,直接循环调用并把结果写入表格即可。
本文还有配套的精品资源,点击获取