
简介这套MATLAB示例面向流体力学初学者与数值计算进阶者围绕二维非定常Navier-Stokes方程完整演示从网格定义、边界条件设置、速度场与压力场初始化到时间推进、压力泊松方程修正及矢量图与流线可视化的求解链路。压缩包共76个文件其中44个.m源码脚本覆盖组装刚度矩阵、对流项、黏性项、载荷向量、梯度算子与形状函数等核心函数29张PNG图为不同时刻速度与压力演化结果另含HTML报告与许可说明整体仅463KB目录结构清晰便于快速定位。已有1932人学习下载。通过可运行代码与配套图形读者能直观理解不可压缩流场的时间离散、投影方法与边界处理技巧模块化的函数设计也方便直接移植到课程设计或科研仿真中作为二次开发的起点。 二维非定常Navier-Stokes方程的MATLAB示例算是计算流体力学入门里绕不开的一块硬骨头。我当年第一次尝试用MATLAB去解这个方程对着公式看了半天觉得都懂结果一运行就发散到飞起最后才意识到方程简洁和代码稳定之间隔着的不是“编程能力”而是对离散格式、边界条件、时间推进稳定性的理解。这篇文章我就用经典的方腔顶盖驱动流Lid-driven cavity作为算例把二维非定常Navier-Stokes从连续方程一路拆到可运行的MATLAB代码顺便把我在调试过程中踩过、并且估计你也会踩的坑都列出来。适合正在学CFD、或者课程作业里要求用MATLAB自己写NS求解器的同学参考。1. 这个示例到底解决了什么问题二维非定常不可压缩Navier-Stokes方程听起来很唬人其实核心就两个量速度场和压力场。但它难在速度的三个分量二维是两个不是独立变化的它们必须满足不可压缩条件也就是速度场始终无散。这个约束让方程从“能解”变成了“有条件的能解”也催生出了一大堆数值处理方法。1.1 方程里的每一项都对应哪段代码先看最常用的原始变量形式其中u为速度矢量p为压力Re为雷诺数。左边的时间偏导和非线性对流项右边是压力梯度项和粘性扩散项。加上不可压条件∇·u0这就是完整控制方程。如果把这个方程和后面的代码对应起来时间偏导对应时间推进循环对流项对应速度场与涡量/速度梯度的乘积扩散项对应二阶中心差分算子压力梯度项则体现在速度修正和压力泊松方程里。之所以强调这个对应关系是因为写代码时很容易“只更新了速度忘了满足无散条件”这是大多数发散问题的根源。1.2 为什么选方腔顶盖流而不是圆柱绕流很多教程喜欢用圆柱绕流展示NS求解器但圆柱绕流要处理曲线边界、尾流涡街、卡门涡脱落入门阶段直接上这个大概率劝退。方腔顶盖驱动流则简单很多矩形计算域、边界都是直线、顶盖以固定速度拖动其他壁面无滑移。即使网格和格式没那么完美也能算出一个主涡旋非常直观。更重要的是这个算例有充分的文献数据和可视化结果可对比。Re100时方腔内会出现一个稳定的主涡涡心大约在坐标(0.62,0.74)附近Re1000时角涡变明显流场会更复杂。所以它既是数值格式的验证平台又是接触非定常流动的入门案例。2. 数值方案为什么绕开投影法二维不可压NS最常见的数值求解思路是“投影法”也叫做压力修正法先不用压力拿当前速度场显式推进一个中间速度再求解压力泊松方程最后用压力梯度修正速度让速度重新满足无散条件。投影法物理清晰但压力边界条件和网格排列处理起来比较麻烦初学时容易在棋盘格振荡上浪费大量时间。2.1 投影法与涡量流函数法的取舍投影法的核心步骤可以写成三步计算中间速度u*不考虑压力梯度求解压力泊松方程∇²p∇·u*/Δt用压力修正出满足无散条件的速度。这种思路非常适合三维扩展因为三维没有流函数这种简单的替代形式。但它也有个隐藏问题如果压力和速度存储在同一个网格点上非交错网格压力泊松方程很容易产生棋盘格振荡也就是压力场呈现出红黑交替的虚假模式看起来像棋盘。要解决这个问题要么使用MAC交错网格要么用Rhie-Chow动量插值对新手来说都是额外负担。所以在这篇示例里我选择的是涡量-流函数形式本质上是二维NS方程的一种等价变换通过取旋度把压力项消掉。方程变成涡量输运方程加流函数泊松方程只需要解一个抛物型方程和一个椭圆型方程不需要处理压力边界条件。代价是无法直接得到压力场后面如果需要压力可以再用已知速度场后处理求解压力泊松方程。方案优点缺点投影法u-p形式易扩展三维可直接得到压力压力边界条件敏感易出现棋盘格涡量流函数法消去压力无压力振荡问题实现简单仅适合二维压力需后处理2.2 边界涡量怎么给才不是乱写用涡量流函数法最容易出错的不是主方程而是边界涡量。涡量的定义是ω∂v/∂x−∂u/∂y在无滑移边界上涡量其实是由壁面切向速度的法向导数决定的。以方腔为例顶盖速度为Uwall1其他壁面速度为0。如果只用一阶单边差分边界涡量的赋值方式是这样的底壁y0u0ω≈−u(2)/Δy顶壁y1uUwallω≈(u(末)−Uwall)/Δy左壁x0v0ω≈v(2)/Δx右壁x1v0ω≈−v(末)/Δx。注意这里的“第二行”和“倒数第二行”是指紧邻边界的内网格点。符号错了流场会出现明显的不对称或直接发散。更精确的做法是用二阶单边差分但在教学示例里一阶已经能稳定工作。3. 直接上代码一个能跑的MATLAB示例这一段给出一个精简但完整的MATLAB实现思路。完整代码我拆成三个部分初始化与网格准备、主时间循环、后处理与验证。网格尺寸取N64Re100初始流场全为零顶盖突然启动。3.1 准备网格和泊松求解器N 64; % 内部网格点数量 Re 100; % 雷诺数 Uwall 1; % 顶盖速度 h 1 / (N 1); % 网格间距 u zeros(N2, N2); % 速度分量 u v zeros(N2, N2); % 速度分量 v psi zeros(N2, N2); % 流函数 omega zeros(N2, N2);% 涡量 u(end, :) Uwall; % 顶盖边界 A gallery(poisson, N); % 离散拉普拉斯算子Dirichlet边界这里的关键是gallery(poisson, N)。它生成的是一个N²×N²的稀疏矩阵对应正方形区域内部网格点的五点差分格式边界值固定为0。因为流函数ψ在四壁都等于0所以只需要对内部网格点求解即可。需要提醒的是这个矩阵对应的是“不带h²缩放”的拉普拉斯算子所以在解泊松方程时右端要乘上h²否则流函数会差一个网格步长的量级速度场也会偏小。3.2 主时间循环的三个关键动作时间推进用一阶显式欧拉格式就能跑空间导数全部用二阶中心差分。稳定性限制取对流CFL和扩散稳定性条件的保守值dt min(0.5*h, 0.25*Re*h^2);Re100、N64时h≈0.0154Re*h²≈0.0237所以dt大概取0.005左右。如果雷诺数再大扩散限制会更严格时间步长会更小。主循环如下nt 2000; dt min(0.5*h, 0.25*Re*h^2); for t 1:nt % 1. 边界涡量 omega(1, :) -u(2, :) / h; omega(end, :) (u(end-1, :) - Uwall) / h; omega(:, 1) v(:, 2) / h; omega(:, end) -v(:, end-1) / h; % 2. 求解流函数 omega_in omega(2:end-1, 2:end-1); psi_vec A \ (h^2 * omega_in(:)); psi(2:end-1, 2:end-1) reshape(psi_vec, N, N); % 3. 由流函数恢复速度 u(2:end-1, 2:end-1) (psi(3:end, 2:end-1) - psi(1:end-2, 2:end-1)) / (2*h); v(2:end-1, 2:end-1) -(psi(2:end-1, 3:end) - psi(2:end-1, 1:end-2)) / (2*h); % 重新施加边界速度 u(1, :) 0; u(end, :) Uwall; u(:, 1) 0; u(:, end) 0; v(1, :) 0; v(end, :) 0; v(:, 1) 0; v(:, end) 0; % 4. 涡量输运方程更新 dwx (omega(2:end-1, 3:end) - omega(2:end-1, 1:end-2)) / (2*h); dwy (omega(3:end, 2:end-1) - omega(1:end-2, 2:end-1)) / (2*h); adv u(2:end-1, 2:end-1) .* dwx v(2:end-1, 2:end-1) .* dwy; lap (omega(2:end-1, 3:end) - 2*omega(2:end-1, 2:end-1) omega(2:end-1, 1:end-2)) / h^2 ... (omega(3:end, 2:end-1) - 2*omega(2:end-1, 2:end-1) omega(1:end-2, 2:end-1)) / h^2; omega(2:end-1, 2:end-1) omega(2:end-1, 2:end-1) dt * (-adv lap / Re); end这个循环里最需要注意的一个细节是速度u和v在边界上的值必须在每次循环里重新覆盖一次。虽然流函数边界恒为0理论上内部速度自动满足无滑移但你在计算内部速度时用到了中心差分边界点本身并不会被更新所以必须显式赋值。我见过不少版本在这里忘了加结果边界条件慢慢失效流场发散。3.3 后处理与结果自检算完之后最直接的可视化是画涡量云图或者速度矢量图[xx, yy] meshgrid(linspace(0,1,N2)); figure; contourf(xx, yy, omega, 20); colorbar; title(Vorticity field);也可以画流函数等值线方腔顶盖流Re100时看起来会有一个很清晰的顺时针主涡。如果想定量验证可以提取竖直中心线上的速度剖面和已有文献数据对比。Re100时沿着x0.5这条线u在靠近顶盖处接近1在腔体中部转为负值形成回流。这个剖面长什么样和文献对得上你的程序基本就是对的。4. 实战避坑发散、振荡、性能一个都别放过这里集中说几个我在调试时踩过并且很多初学者容易反复踩的问题。每个问题背后都有逻辑知道了原因调试起来就不会像无头苍蝇。4.1 算着算着就发散先查这三点第一检查时间步长是否满足稳定性条件。显式格式下如果对流CFL数大于1或扩散项νΔt/h²大于0.5高频振荡会被逐步放大然后快速发散。保守一点把dt再缩小一倍如果流场稳定了就是dt问题否则继续查。第二检查泊松求解的缩放系数。很多人用gallery(poisson)时忘了乘h²导致流函数和速度场整体缩小看起来像是“没有流动”。反过来如果多乘了一个1/h²速度场又会被放大几倍也会触发数值失稳。第三检查边界涡量符号。特别是左右壁面的v导数方向很容易因为索引顺序搞反。一个快速测试方法是把顶盖速度设为0看静止流场是否真的保持不变如果静止流场出现了涡量说明边界涡量或速度恢复过程里肯定有错。4.2 为什么没有压力棋盘格问题以及怎么处理在涡量流函数形式里连续性约束已经通过流函数自动满足了所以不存在压力棋盘格振荡。如果你用投影法求解原始变量形式出现棋盘格是常事这往往是因为速度和压力存储在同一个网格点上。解决办法要么换成MAC交错网格要么对压力项使用Rhie-Chow插值要么在压力泊松方程里用足够的五点格式并配合必要的滤波。如果你只是需要一个能交作业的稳定示例用涡量流函数法是最省心的。但如果后续课题需要三维和压力信息那还是要回到投影法到那个时候再学交错网格也不迟。4.3 提速经验和下一步扩展这个示例里每个时间步都调用了A \ psi_vec对N64的网格毫无压力但如果你把N加到128甚至256这种“每步直接分解”的方式会明显变慢。更好的做法是在循环外先做一次矩阵分解[Lmat, Umat] lu(A); % 循环内 psi_vec Umat \ (Lmat \ (h^2 * omega_in(:)));这样可以省掉每步重新分解矩阵的开销。更彻底的方案是用SOR迭代或者FFT求解泊松方程前者内存占用小后者在均匀网格上几乎可以做到O(N²logN)的速度。如果你想把这个例子扩展下去有几个方向改成非定常圆柱绕流需要引入浸没边界或者贴体网格把时间推进换成RK3或RK4在相同时间步长下可以获得更高精度加入压力后处理从已知速度场求解压力泊松方程从而得到完整的NS解。每一步都不难但都需要先把手上的这个基础示例彻底理解。我在最初用这个涡量流函数框架时犯过最无语的一个错误是初始化时忘了给顶盖边界速度赋值导致算出来整个流场全是零。后来我把代码里“初始条件、边界条件、循环内更新、循环后重新施加边界条件”这四个阶段分开来检查才慢慢养成调试CFD程序的节奏。如果你也是刚接触建议先跑通这个示例再手动改Re和网格数感受一下非定常流场的变化规律这比直接追着最新算法要扎实得多。本文还有配套的精品资源点击获取