1. 项目概述与背景
在计算流体力学(CFD)领域,Navier-Stokes(NS)方程的数值求解一直被视为"皇冠上的明珠"。许多工程师习惯依赖商业软件如Fluent或OpenFOAM,却鲜少了解底层算法的实现细节。本文将完整展示如何从零开始用MATLAB构建二维NS方程求解器,实现速度场和压力场的同步计算。
这个自研求解器的核心价值在于:
- 完全透明可控:每行代码都对应明确的物理意义和数学操作
- 教学科研两用:既适合CFD初学者理解基本原理,也可作为科研快速原型工具
- 灵活定制:网格尺寸、边界条件、物性参数均可自由调整
注意:本文代码适用于层流模拟(Re<1000),如需处理湍流需额外添加模型
2. 理论基础与算法设计
2.1 控制方程体系
二维不可压缩流动的控制方程包括:
质量守恒方程:
∇·u = 0
动量守恒方程:
∂u/∂t + (u·∇)u = -∇p/ρ + ν∇²u + f
其中关键参数:
- u = (u,v):速度矢量(m/s)
- p:压力(Pa)
- ρ:密度(kg/m³),本文取常数
- ν:运动粘度(m²/s)
2.2 数值方法选型
采用SIMPLE算法族中的SIMPLER变种,主要优势:
- 压力-速度耦合求解稳定性好
- 适合结构化网格实现
- 收敛性可通过松弛因子调节
离散方案对比:
| 项 | 格式 | 原因 |
|---|---|---|
| 时间项 | 显式欧拉 | 实现简单,适合教学 |
| 对流项 | 迎风格式 | 保持有界性 |
| 扩散项 | 中心差分 | 二阶精度 |
| 压力梯度 | 中心差分 | 保证动量守恒 |
2.3 交错网格技术
关键创新点:将标量(p)和矢量(u,v)存储在不同位置
- 压力p:存储在网格中心(●)
- 速度u:存储在垂直面中心(→)
- 速度v:存储在水平面中心(↑)
这种布置有效避免了著名的"棋盘式压力震荡"问题。
3. 代码实现详解
3.1 计算域初始化
matlab复制% 几何参数
Lx = 2; Ly = 1; % 域尺寸(m)
Nx = 51; Ny = 26; % 网格数
dx = Lx/(Nx-1); dy = Ly/(Ny-1);
% 生成交错网格坐标
x = linspace(0, Lx, Nx);
y = linspace(0, Ly, Ny);
[X, Y] = meshgrid(x, y);
% 场变量初始化
u = zeros(Ny, Nx); % x-velocity
v = zeros(Ny, Nx); % y-velocity
p = zeros(Ny, Nx); % pressure
3.2 边界条件设置
入口采用抛物线速度分布:
matlab复制% 入口边界 (左边界)
u_inlet = 1.5 * (1 - (Y(:,1)/Ly - 0.5).^2);
u(:,1) = u_inlet;
% 壁面边界 (无滑移)
u(1,:) = 0; u(end,:) = 0;
v(1,:) = 0; v(end,:) = 0;
% 出口边界 (右边界, 充分发展)
u(:,end) = u(:,end-1);
v(:,end) = v(:,end-1);
p(:,end) = 0; % 参考压力
3.3 动量方程求解
x方向动量方程离散示例:
matlab复制for i = 2:Nx-1
for j = 2:Ny-1
% 对流项 (迎风)
ue = 0.5*(u(j,i+1) + u(j,i));
uw = 0.5*(u(j,i) + u(j,i-1));
un = 0.5*(u(j+1,i) + u(j,i));
us = 0.5*(u(j,i) + u(j-1,i));
conv_u = (ue^2 - uw^2)/dx + (un*v(j,i) - us*v(j-1,i))/dy;
% 扩散项 (中心差分)
diff_u = nu*( (u(j,i+1)-2*u(j,i)+u(j,i-1))/dx^2 ...
+ (u(j+1,i)-2*u(j,i)+u(j-1,i))/dy^2 );
% 压力梯度
dpdx = (p(j,i+1) - p(j,i))/dx;
% 时间推进
u_new(j,i) = u(j,i) + dt*(-conv_u + diff_u - dpdx);
end
end
3.4 压力修正算法
SIMPLER核心迭代流程:
matlab复制while max_divergence > 1e-5
% 1. 求解压力泊松方程
for iter = 1:max_p_iter
p_old = p;
for i = 2:Nx-1
for j = 2:Ny-1
p(j,i) = ( (p(j,i+1)+p(j,i-1))/dx^2 + (p(j+1,i)+p(j-1,i))/dy^2 ...
- divergence(j,i) ) * dx^2*dy^2 / (2*(dx^2+dy^2));
end
end
p = p_old + omega*(p - p_old); % 亚松弛
end
% 2. 速度修正
u(2:end-1,2:end-1) = u_star(2:end-1,2:end-1) - dt*diff(p(:,2:end),1,2)/dx;
v(2:end-1,2:end-1) = v_star(2:end-1,2:end-1) - dt*diff(p(2:end,:),1,1)/dy;
% 3. 更新发散度
divergence = compute_divergence(u,v,dx,dy);
max_divergence = max(abs(divergence(:)));
end
4. 关键参数与优化技巧
4.1 稳定性条件
CFL条件:
dt ≤ min(dx/|u|max, dy/|v|max)
扩散限制:
dt ≤ 0.25*min(dx², dy²)/ν
建议采用自适应时间步长:
matlab复制umax = max(abs(u(:))); vmax = max(abs(v(:)));
dt_cfl = 0.5 * min(dx/umax, dy/vmax);
dt_diff = 0.25 * min(dx^2, dy^2)/nu;
dt = min(dt_cfl, dt_diff);
4.2 加速收敛技巧
-
亚松弛因子:
- 压力:ωp = 0.7~1.0
- 速度:ωu = ωv = 0.5~0.7
-
多重网格法:
在粗网格上快速消除低频误差,再在细网格修正 -
矢量化优化:
替换嵌套循环为矩阵运算
matlab复制% 传统循环
for i = 2:Nx-1
for j = 2:Ny-1
p(j,i) = ...;
end
end
% 矢[量化版本](https://taotoken.net?utm_source=general)
i = 2:Nx-1; j = 2:Ny-1;
p(j,i) = ( (p(j,i+1)+p(j,i-1))/dx^2 + ... ) * dx^2*dy^2./(2*(dx^2+dy^2));
5. 后处理与可视化
5.1 流场绘制
matlab复制% 速度场 (quiver)
[Xc, Yc] = meshgrid(x(1:end-1)+dx/2, y(1:end-1)+dy/2); % 速度点位置
quiver(Xc, Yc, u(1:end-1,1:end-1), v(1:end-1,1:end-1), 2);
hold on;
% 压力场 (contourf)
contourf(X, Y, p, 20, 'LineColor', 'none');
colorbar;
5.2 收敛监测
matlab复制% 计算质量守恒残差
divergence = (u(:,2:end) - u(:,1:end-1))/dx + ...
(v(2:end,:) - v(1:end-1,:))/dy;
residual = norm(divergence(:), 2);
semilogy(iter_history, residual_history, '-o');
xlabel('迭代次数'); ylabel('连续方程残差');
6. 典型问题排查
6.1 发散问题处理
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 压力震荡 | 松弛因子过大 | 降低ωp至0.3-0.5 |
| 速度爆炸 | 时间步长过大 | 检查CFL条件,减小dt |
| 残差不降 | 边界条件错误 | 验证入口/出口设置 |
| 不对称解 | 网格不均匀 | 检查dx/dy一致性 |
6.2 性能优化记录
- 预计算系数:
将重复使用的几何系数预先计算存储
matlab复制% 预先计算差分系数
dx2 = 1/dx^2; dy2 = 1/dy^2;
dxy = dx2*dy2/(2*(dx2+dy2));
-
稀疏矩阵:
对于大型网格,使用sparse矩阵求解压力方程 -
并行计算:
使用parfor加速动量方程计算
7. 扩展应用方向
当前求解器可进一步扩展:
-
湍流模型:
- 添加k-ε或LES模型
- 实现壁面函数处理
-
多相流:
- VOF法追踪自由表面
- 添加表面张力模型
-
传热耦合:
- 加入能量方程
- 处理自然对流
这个自研NS求解器就像一套乐高积木,每个模块都可以独立改进。当看到自己编写的代码成功再现流体运动的优美图案时,那种透过代码触摸物理规律的感觉,正是计算流体力学最迷人的地方。