☰
一维河道水动力模型:圣维南方程与MATLAB实现
2026/10/27 8:02:51 网站建设 项目流程

简介:这是一份面向水文学与水利工程方向学习者的Matlab一维水动力模型代码资源,聚焦矩形河道内流速与水深的数值计算。模型以圣维南方程组为基础,通过连续性方程与动量方程描述一维流动,并考虑入口、出口、自由表面等边界条件以及糙率系数对摩擦损失的影响,可用于河流管理、洪水预警与水利工程设计等场景,适合具备Matlab编程基础和水动力学知识的本科高年级学生、研究生及工程技术人员参考。压缩包共4个文件,包含2个m脚本文件与2个xlsx数据文件,前者承担初始化、主程序求解与结果输出等功能,后者提供计算所需的输入数据,整体约323KB,结构精简便于快速上手。目前已有1603人学习下载,读者可据此理解一维河道水流的数值求解流程,掌握有限差分等离散思路,并在此基础上修改参数、扩展边界条件,为实际河道水流特性分析与水环境管理提供可复用的计算工具。

1. 一维河道水动力模型:从圣维南方程到可跑的 matlab 代码

汛期河道水位预报、灌区引水调度、山洪演进分析,绕不开的一个基础工具就是一维河道水动力模型。它做的事很朴素:给定上游流量过程、下游水位边界和河道断面地形,算出沿程各断面的水位和流量随时间怎么变。核心方程是圣维南方程组,由连续方程和动量方程组成,本质是把质量守恒和动量守恒写成一维形式。matlab 在这里的角色不是画图工具,而是把偏微分方程离散、组装、迭代求解的完整计算平台。适合谁?做水文预报、水利工程设计、防洪评价的工程师,以及需要快速验证算法而不想碰大型商业软件的研究生。这篇文章不讲空泛理论,直接给出一套能跑通、能改参数、能定位报错的 matlab 实现路径,包括离散格式选择、边界处理、稳定性控制和常见翻车点。

2. 圣维南方程怎么离散:四种格式的选型与 matlab 落地

2.1 为什么显式格式容易炸而隐式格式更稳

一维河道水动力模型的数值求解,第一步是决定用显式还是隐式。显式格式(如 Lax-Wendroff、MacCormack)每个时间步直接由已知量算出未知量,代码简单,但受 CFL 条件严格约束:时间步长必须小于波速传播一个网格所需的时间。河道里洪水波速约 3~5 m/s,若空间步长 100 m,时间步长不能超过 20~30 s,遇到陡坡或急流段还会更小。一旦超限,计算立刻发散,水位出现非物理振荡。

隐式格式(如 Preissmann 四点偏心格式)在每个时间步需要联立求解整条河道的方程组,计算量大,但无条件稳定,时间步长可以取到 300~600 s 甚至更大。对于长历时洪水演进模拟,隐式格式的总计算时间反而更短。我一般这样选:如果河道长度不超过 20 km、模拟时长在几小时以内,用显式格式快速验证;如果河道上百公里、要算几天过程,直接上 Preissmann 隐式格式。

Preissmann 格式的核心是把变量在时间和空间上做加权平均。设权重系数 θ(时间加权)和 ψ(空间加权),θ 取 0.5~1.0,ψ 取 0.5 时精度较好。θ=1.0 时退化为全隐式,稳定性最好但时间精度降到一阶。实际工程中 θ=0.6~0.7 是常用值,兼顾稳定和精度。

2.2 用 matlab 组装系数矩阵的最小代码框架

下面给出 Preissmann 格式离散后组装系数矩阵的核心代码。假设河道被划分为 N 个断面,每个断面有水位 Z 和流量 Q 两个未知量,共 2N 个未知数。每个内部断面提供两个方程(连续方程和动量方程),加上上下游各一个边界条件,恰好封闭。

% 一维河道水动力模型 - Preissmann格式系数矩阵组装 % 输入: N-断面数, dt-时间步长(s), dx-空间步长(m) % 输出: A-系数矩阵(2N x 2N), b-右端项(2N x 1) function [A, b] = assemble_preissmann(N, dt, dx, Z_old, Q_old, params) % params 包含: B-水面宽, A_flow-过水面积, R-水力半径, n_manning-糙率 g = 9.81; theta = 0.65; % 时间加权系数 psi = 0.5; % 空间加权系数 n_eq = 2 * N; A = sparse(n_eq, n_eq); b = zeros(n_eq, 1); for i = 1:N-1 % 连续方程离散 (第 i 和 i+1 断面之间) row_c = 2*i - 1; % 对 Z_i, Q_i, Z_{i+1}, Q_{i+1} 的偏导系数 B_avg = 0.5 * (params.B(i) + params.B(i+1)); A(row_c, 2*i-1) = theta * B_avg * dx / (2*dt) + psi; A(row_c, 2*i) = theta; A(row_c, 2*i+1) = theta * B_avg * dx / (2*dt) - (1-psi); A(row_c, 2*i+2) = -theta; % 右端项: 上一时刻的值贡献 b(row_c) = (1-theta) * (Q_old(i) - Q_old(i+1)) ... + theta * B_avg * dx / (2*dt) * (Z_old(i) + Z_old(i+1)); % 动量方程离散 row_m = 2*i; Q_avg = 0.5 * (abs(Q_old(i)) + abs(Q_old(i+1))); A_avg = 0.5 * (params.A_flow(i) + params.A_flow(i+1)); K = A_avg * params.R(i)^(2/3) / params.n_manning; % 输水能力 A(row_m, 2*i-1) = -theta * g * A_avg / dx * psi; A(row_m, 2*i) = theta * (1 + g * abs(Q_avg) * dt / (K^2)); A(row_m, 2*i+1) = -theta * g * A_avg / dx * (1-psi); A(row_m, 2*i+2) = theta * (1 - g * abs(Q_avg) * dt / (K^2)); b(row_m) = (1-theta) * (Q_old(i) - Q_old(i+1)) ... + theta * g * A_avg * dt / dx * (Z_old(i) - Z_old(i+1)) ... - theta * g * A_avg * dt * params.S0(i); % S0-底坡 end % 上游边界: 给定流量过程 Q(1) = Q_up(t) A(2*N-1, 2) = 1.0; b(2*N-1) = params.Q_up; % 下游边界: 给定水位 Z(N) = Z_down(t) A(2*N, 2*N-1) = 1.0; b(2*N) = params.Z_down; end

这段代码的逻辑说明:循环遍历每个内部断面,连续方程控制流量沿程变化与水位变化的关系,动量方程控制流速变化与压力梯度、摩阻、底坡的平衡。系数矩阵采用稀疏存储,因为 N 通常几百到几千,稠密矩阵会吃掉大量内存。右端项里保留了上一时间步的贡献,这是隐式格式的典型结构。

参数说明:theta控制时间离散权重,取 0.5 是 Crank-Nicolson 格式(二阶精度但可能振荡),取 1.0 是全隐式(一阶精度但绝对稳定),工程上 0.6~0.7 是甜点区。psi控制空间加权,0.5 为中心差分,偏离 0.5 会引入数值耗散。n_manning是曼宁糙率,主河道一般 0.025~0.035,滩地 0.04~0.06。dx建议取 50~200 m,太密会导致矩阵规模爆炸,太疏会抹平河道地形细节。

2.3 时间推进与收敛判据怎么设

组装完系数矩阵后,每个时间步解一次线性方程组A * x = b,其中x是待求的[Z1, Q1, Z2, Q2, ...]向量。matlab 里直接用x = A \ b即可,稀疏矩阵会自动调用合适的求解器。但要注意:如果河道有急流或水跃,动量方程的非线性项可能导致迭代不收敛。这时需要引入 Newton-Raphson 迭代,每步更新一次系数矩阵。

收敛判据一般设两个:水位变化量小于 0.001 m,流量变化量小于 0.01 m³/s。最大迭代次数设 10~20 次,超过就说明时间步长太大或参数不合理。我习惯在代码里加一个诊断输出,记录每步的迭代次数,如果连续多步都接近最大迭代次数,就该考虑减小dt或调整theta。

3. 边界条件与初始场:模型能不能跑起来的关键

3.1 上游流量和下游水位怎么给才不报错

一维河道水动力模型的边界条件组合,常见的有三种:上游给流量、下游给水位(最常用);上游给水位、下游给流量(适用于闸控河道);上下游都给水位(适用于潮汐河段)。对于洪水演进模拟,上游流量过程来自水文站实测或水文模型输出,下游水位来自水位站或水位-流量关系曲线。

上游流量边界在代码里就是Q(1) = Q_up(t),直接赋值即可。但要注意:如果上游流量突变太大(比如从 100 m³/s 跳到 500 m³/s),隐式格式虽然不会发散,但会出现数值振荡。实际处理时,我会对流量过程做 3~5 个时间步的线性渐变,或者直接用实测的逐时流量,避免人为突变。

下游水位边界稍微麻烦一点。如果下游是自由出流,水位由流量通过率定曲线确定;如果下游有闸门或泵站,水位是已知的时间序列。代码里Z(N) = Z_down(t)直接赋值。但有个坑:下游水位不能低于河道最低点,否则过水面积变成负数,计算直接崩溃。我一般会在赋值前加一个判断,如果Z_down < Z_bottom(N),就取Z_bottom(N) + 0.01。

3.2 初始场用干河床还是预热水位

初始条件决定模型启动时的状态。如果河道初始是干的,所有断面水位设成河底高程,流量设成 0。这种设置简单,但启动后前几十个时间步会有剧烈调整,水位从河底快速抬升,容易触发数值振荡。更稳妥的做法是用一个预热期:先给一个较小的恒定流量跑 1~2 天,让水位和流量达到平衡,再开始正式模拟。

预热期的代码实现很简单:把上游流量设成常年平均流量,下游水位设成对应常水位,跑 500~1000 个时间步,然后把结果作为正式模拟的初始场。多花几分钟计算时间,换来的是后续模拟的稳定。我做过对比,不预热直接算洪水过程,前 2 小时的水位误差能到 0.3~0.5 m;预热后误差降到 0.05 m 以内。

3.3 断面地形数据的插值与平滑

实际河道断面测量数据往往是离散的,测点间距不均匀。模型需要的是规则网格上的断面参数(水面宽、过水面积、水力半径)。常见做法是用三次样条插值把实测断面加密到dx间隔,然后计算每个断面的几何参数。

% 断面地形插值与几何参数计算 % x_meas, z_meas: 实测断面起点距和高程 % x_grid: 目标网格起点距 function [B, A_flow, R] = interp_section(x_meas, z_meas, x_grid, Z_water) % 三次样条插值地形 z_grid = interp1(x_meas, z_meas, x_grid, 'spline'); % 计算水面宽和过水面积 B = zeros(size(x_grid)); A_flow = zeros(size(x_grid)); R = zeros(size(x_grid)); for i = 1:length(x_grid) wet = z_grid(i) < Z_water; % 淹没区域 if sum(wet) < 2 B(i) = 0.01; % 防止除零 A_flow(i) = 0.001; R(i) = 0.001; else B(i) = sum(wet) * (x_grid(2) - x_grid(1)); A_flow(i) = trapz(x_grid(wet), Z_water - z_grid(wet)); % 湿周近似为水面宽加两倍水深 P = B(i) + 2 * mean(Z_water - z_grid(wet)); R(i) = A_flow(i) / P; end end end

这段代码的关键点:interp1用样条插值避免线性插值造成的折角,折角会导致动量方程里的底坡项出现尖峰。wet逻辑判断哪些网格点被淹没,未淹没的给一个极小值防止除零错误。trapz用梯形法算过水面积,比矩形法精度高。湿周的计算是近似,严格来说应该沿湿周积分,但一维模型里这个近似够用。

注意:断面数据里如果有倒挂(下游断面河底高于上游),插值后会出现负底坡,动量方程里的S0项变成负值,可能导致计算不稳定。遇到这种情况,先对底坡做平滑处理,或者把倒挂段单独处理。

4. 避坑与排查:一维河道模型最常见的五个翻车现场

4.1 计算发散:水位出现 NaN 或 1e10 量级

现象:跑了几十个时间步后,水位突然变成 NaN 或者 1e10 这种离谱数值。原因通常是 CFL 条件不满足(显式格式)或者系数矩阵奇异(隐式格式)。隐式格式虽然无条件稳定,但如果边界条件设置矛盾(比如上游流量给得太大、下游水位给得太低),矩阵条件数会急剧恶化。

解决:先检查dt和dx的比值,显式格式要求dt < dx / (|v| + sqrt(g*h))。隐式格式检查边界条件是否物理合理,上游流量对应的水深不能超过河道堤顶。如果都没问题,把theta从 0.6 调到 0.8 或 1.0,增加数值耗散。还不行就减小dt到原来的一半。

4.2 质量不守恒:进出水量对不上

现象:模拟结束后,上游总进水量减去下游总出水量,不等于河道槽蓄量的变化。原因可能是连续方程离散时,水面宽B的处理不一致。Preissmann 格式里B应该取时间加权平均值,如果代码里用了上一时刻的B,就会引入质量误差。

解决:检查连续方程里B_avg的计算,确保用的是theta * B_new + (1-theta) * B_old。另外,边界处的流量赋值要和时间步对齐,不能把t时刻的流量用到t+dt时刻的方程里。

4.3 水位振荡:结果出现锯齿状波动

现象:水位过程线不平滑,出现高频锯齿。原因通常是空间步长太大,或者psi偏离 0.5 太多。中心差分(psi=0.5)不引入数值耗散,但会在陡坡段产生振荡。上游式(psi>0.5)引入耗散,能抑制振荡但会抹平峰值。

解决:先尝试把psi调到 0.55~0.6,增加一点数值耗散。如果振荡还在,把dx减小到原来的一半。还不行就检查断面地形插值,样条插值在突变处会产生过冲,改用保形插值(interp1的'pchip'选项)。

4.4 负水深:过水面积变成负数

现象:某个断面的水位低于河底高程,A_flow变成负数,计算崩溃。原因可能是下游水位边界给得太低,或者初始场水位设得太低。隐式格式里,如果下游水位低于河底,系数矩阵会出现负的对角元。

解决:在代码里加保护,每次更新水位后检查Z(i) < Z_bottom(i),如果成立就强制Z(i) = Z_bottom(i) + 0.01。同时检查下游边界,确保Z_down不低于下游断面的河底高程。如果是干河床启动,初始水位就设成河底加 0.01。

4.5 计算太慢:跑一天过程要几个小时

现象:N=500 的河道,模拟 72 小时,跑了 3 小时还没结束。原因可能是用了稠密矩阵求解,或者时间步长太小。A \ b对稀疏矩阵会自动优化,但如果A是稠密的,500×2=1000 阶矩阵每步求解就要几秒。

解决:确保A = sparse(n_eq, n_eq)初始化。检查dt是否取到了稳定允许的最大值,隐式格式dt可以取到 300~600 s。如果还是慢,把dx从 50 m 放宽到 100 m,断面数减半,计算量降到四分之一。精度损失在洪水演进模拟里通常可以接受。

5. 进阶技巧:用实测数据率定糙率并验证模型

模型能跑通只是第一步,结果准不准才是关键。一维河道水动力模型最敏感的参数是曼宁糙率n,它直接控制洪水波的传播速度和峰值衰减。率定的思路很简单:找一场实测洪水,上游流量过程和下游水位过程都有记录,调整各河段的n值,让模拟水位和实测水位的误差最小。

我一般分三步走。第一步,把河道按断面形态分成 3~5 个河段,每段给一个初始n值,主河道取 0.03,滩地取 0.045。第二步,跑模拟,计算各水位站的均方根误差 RMSE。第三步,用简单的试错法或 matlab 的fminsearch优化n值,目标函数就是 RMSE 之和。

% 糙率率定: 用 fminsearch 最小化水位RMSE % n_init: 各河段初始糙率向量 % Z_obs: 实测水位矩阵 (时间 x 站点) % Z_sim_func: 调用水动力模型的函数句柄 function n_opt = calibrate_manning(n_init, Z_obs, Z_sim_func) options = optimset('Display', 'iter', 'TolX', 1e-4); obj_func = @(n) compute_rmse(n, Z_obs, Z_sim_func); n_opt = fminsearch(obj_func, n_init, options); end function rmse_total = compute_rmse(n, Z_obs, Z_sim_func) Z_sim = Z_sim_func(n); % 跑一次模型 rmse_total = 0; for k = 1:size(Z_obs, 2) rmse_total = rmse_total + sqrt(mean((Z_sim(:,k) - Z_obs(:,k)).^2)); end end

这段代码的逻辑:fminsearch是 matlab 内置的单纯形优化器,不需要计算梯度,适合这种目标函数没有解析形式的场景。compute_rmse里每调用一次就要跑一遍完整的水动力模型,所以率定过程可能比较耗时。实际使用时,我会先用粗网格(dx=200 m)快速率定,再用细网格(dx=50 m)微调。

参数说明:n_init的维度等于河段数,一般 3~5 个就够了,太多会导致参数不唯一。TolX控制收敛精度,1e-4 对应糙率变化 0.0001,足够工程精度。如果率定后 RMSE 还是大于 0.2 m,说明问题不在糙率,而在断面地形或边界条件,需要回头检查输入数据。

验证模型时,我习惯留一场洪水不参与率定,专门做验证。如果率定期和验证期的 RMSE 都在 0.1~0.15 m 以内,模型就可以用了。如果验证期误差明显大于率定期,说明糙率过拟合了,需要减少河段数或者固定部分河段的糙率。

最后说一个血泪教训:不要迷信自动率定。有次我让fminsearch自己跑,结果它把上游河段的糙率调到 0.01,下游调到 0.08,RMSE 确实降了,但物理上完全不合理。后来我加了约束,主河道糙率限制在 0.02~0.04,滩地限制在 0.035~0.06,率定结果才靠谱。模型参数可以优化,但不能脱离物理意义。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询