多孔介质LBM模拟全攻略:从D2Q9模型到渗透率与迂曲度计算
简介格子玻尔兹曼方法LBM在多孔介质流体模拟中的应用是计算流体力学的重要方向这份基于LBM_P的MATLAB代码面向煤炭、石油、岩土等领域科研人员与工程师适用于渗流分析、污染物迁移及流固耦合等场景。包内共1个m文件压缩包约3KB代码结构紧凑便于直接读取并调整边界条件、渗透率等参数借此重现多孔介质中的速度场与压力分布也可作为拓展LBM模型的起点。目前已有1018人学习下载适合具备一定流体力学与MATLAB基础、希望快速上手LBM_P算法的中高级使用者。通过研读代码可更直观理解LBM_P如何处理复杂孔隙结构与固液界面并围绕瓦斯排放预测、注水采油优化、地下水污染扩散评估等实际工程问题开展数值实验。1. 多孔介质流动为什么找上格子玻尔兹曼地下水的迁移、燃料电池气体扩散层里的氧气输运、催化剂载体里的反应物扩散最终都要归结到孔隙尺度的流动。这类几何随机、孔道曲折、边界复杂的算例用宏观 CFD 先吃一个贴体网格的生成往往比解方程本身还费劲。格子玻尔兹曼方法Lattice Boltzmann MethodLBM恰好把这一步绕开了它用规则格点加一个布尔数组记录固体位置流体在格点上碰撞、迁移遇到固体格点直接反弹。LBM_P 里这个 P 指的就是 Porous同一套 D2Q9 内核把几何换成多孔骨架就能算渗透率、迂曲度和内部流场。这套路径适合做渗流力学、岩心分析、燃料电池和过滤材料的人新手按格点逻辑也能把结果跑出来。2. 格子玻尔兹曼的孔道视角从 BGK 碰撞到多孔骨架边界2.1 从分布函数到宏观流速D2Q9 与 BGK 的演化多孔介质里的流动一般雷诺数很小很多场景甚至落在蠕动流区间宏观上仍由纳维-斯托克斯方程支配。但 LBM 不直接解 NS 方程组而是追踪粒子分布函数 (f_i)在二维最常用 D2Q9 离散速度模型。演化过程可以压缩成三步碰撞、迁移、外力修正。BGK 单松弛碰撞算子的演化方程写出来就是[ f_i(xc_i \Delta t, t\Delta t) f_i(x,t) - \frac{1}{\tau}\left(f_i - f_i^{eq}\right) F_i ]其中 (\tau) 是无量纲松弛时间与运动粘度 (\nu) 的关系在 D2Q9 里是 (\nu (\tau-0.5)/3)。宏观密度和速度通过分布函数的零阶矩和一阶矩恢复[ \rho \sum_i f_i, \quad u \frac{1}{\rho}\sum_i f_i c_i ]D2Q9 的九个离散速度方向是0,0、(±1,0)、(0,±1)、(±1,±1)对应的权重分别为 4/9、1/9、1/36。这个权重表格是整套代码的起点。方向索引 i速度分量 (ex, ey)权重 w_i0(0, 0)4/91, 2(±1, 0)1/93, 4(0, ±1)1/95, 6, 7, 8(±1, ±1)1/36平衡分布函数是 LBM 把介观量拉回宏观流场的桥梁。下面这段代码把平衡分布写成可复用的函数之后不管几何怎么换这层不用动。def equilibrium_d2q9(rho, ux, uy, w, ex, ey): # 计算 D2Q9 的平衡分布函数 feq np.zeros((w.size,) rho.shape) u2 ux * ux uy * uy for i in range(w.size): eu ex[i] * ux ey[i] * uy feq[i] w[i] * rho * (1.0 3.0 * eu 4.5 * eu * eu - 1.5 * u2) return feq这段代码的核心逻辑是让平衡分布随局部密度和速度重新分配权重。初始时若流场速度为零rho 取 1.0那么九个方向的分布正好就是权重本身速度起来后eu 的高阶项会补偿动量的方向性。参数里 tau 通常取 0.55~1.0因为 (\tau) 太靠近 0.5 时粘度近似为零数值上会出现高频振荡取太大则流动进入强耗散区压力梯度需要放很大才能推动流体导致低速区域的舍入误差放大。2.2 生成多孔几何的三种路径规则阵列、随机圆盘、数字岩心多孔介质 LBM 的“几何”说到底就是一张布尔掩膜固体格点记 True流体格点记 False。换结构不用改求解器只换掩膜这是 LBM 在多孔问题上比贴体网格方案省事得多的根本原因。常见做法有三条路径。建模方式几何来源孔隙率控制难度适用场景规则阵列圆柱、球形障碍周期性排布容易可用解析体积公式算法验证、边界格式对比随机圆盘/球体重叠程序化生成随机投点中等需要迭代调整密度算例开发、参数敏感性研究数字岩心CT/MRI 扫描后二值化由真实结构决定岩心、电极、骨组织等真实样本规则阵列适合先检验代码一排圆柱排成正方形或菱形Darcy 渗透率在文献里有对照值跑完能确认边界格式没有系统性偏差。随机圆盘是开发阶段性价比最高的选择生成快且孔隙率和孔喉分布都接近天然松散介质。数字岩心最接近真实但需要先做图像滤波、连通域分析和分辨率判断如果喉道直径只有四五个格点LBM 的反弹边界误差会盖过真实信号这时候再好的 CT 数据也救不回来。2.3 多孔介质边界的反弹细节有效壁面位置与驱动方式LBM 处理固体边界的最简单方案是反弹边界迁移时分布函数进入固体格点后沿来路反向弹回。这种格式的实现几乎不增加计算量但代价是有效壁面位置不在流体格点中心也不在固体格点中心而是在两者之间的中点。对孔隙介质来说这意味着一个名义上 8 格宽的喉道实际流动截面可能只有 7 格渗透率偏差可能达到百分之十几。孔隙越窄这个偏差越致命。所以多孔介质 LBM 的一个前置要求是喉道宽度至少要 10 个格点经验上取 15~20 格更稳妥。驱动方式同样影响结果。入口出口做成压力边界时Zou-He 格式的数值反射容易在孔隙内部激起微弱的伪振荡收敛后平均速度也许看不出问题但局部流场会带着非物理条纹。更稳的做法是在整个计算域施加恒定的体积力等效于一个均匀压力梯度配合周期边界实现无限长多孔介质段。体积力驱动的另一个好处是压力梯度的数值大小直接由外力强度给出后续由达西定律反算渗透率时少一道对进出口压差的统计误差。3. 组装一个可以算渗透率的多孔介质 LBM 流程3.1 用随机圆盘生成带目标孔隙率的多孔骨架生成多孔几何时最常遇到的问题是孔隙率看起来对实际流动通道却被少量“死胡同”占掉一大块。以下代码生成非重叠随机圆盘骨架并用取模操作让圆盘跨边界时自动周期复制保证后面周期边界可用。import numpy as np nx, ny 256, 256 target_phi 0.65 radius 4 rng np.random.default_rng(2024) solid np.zeros((nx, ny), dtypebool) needed int((1.0 - target_phi) * nx * ny) while solid.sum() needed: cx, cy rng.integers(0, nx), rng.integers(0, ny) for dx in range(-radius, radius 1): for dy in range(-radius, radius 1): if dx * dx dy * dy radius * radius: # 取模实现周期复制避免边界处圆盘被截断 x, y (cx dx) % nx, (cy dy) % ny solid[x, y] True porosity 1.0 - solid.mean() print(actual porosity:, porosity)这段循环每次投一个圆盘每次增加约 (\pi r^2) 个固体格点所以实际孔隙率会略低于目标值。若要精确到小数点后两位可以在投盘前预计算本次新增格点数超出 needed 时跳过一次投放。这里的 radius 是圆盘半径的格点数量取 4 时喉道宽度约为 8 格只适合快速演示正式计算建议把圆盘半径放大到 8~10 格同时把计算域扩大到 512 以上否则孔隙率统计和渗透率结果都不可信。3.2 碰撞、迁移、反弹三段主循环的实现有了掩膜后主循环是标准的碰撞-迁移-反弹三步。碰撞前先恢复宏观量碰撞时只处理流体格点迁移用 np.roll 实现周期位移反弹在固体格点内交换反向分布函数。# D2Q9 基础数组 w np.array([4/9, 1/9, 1/9, 1/9, 1/9, 1/36, 1/36, 1/36, 1/36]) ex np.array([0, 1, -1, 0, 0, 1, -1, 1, -1]) ey np.array([0, 0, 0, 1, -1, 1, -1, -1, 1]) opp np.array([0, 2, 1, 4, 3, 6, 5, 8, 7]) tau 0.8 Fx 1e-5 # 体积力等效压力梯度 fluid ~solid f np.zeros((9, nx, ny)) for i in range(9): f[i] w[i] # 初始化为均匀平衡分布rho1u0 for step in range(100000): # 恢复宏观量 rho f.sum(axis0) ux (f[1] - f[2] f[5] - f[6] f[7] - f[8]) / rho uy (f[3] - f[4] f[5] f[6] - f[7] - f[8]) / rho feq equilibrium_d2q9(rho, ux, uy, w, ex, ey) # 碰撞只对流体格点 f[:, fluid] f[:, fluid] - (f[:, fluid] - feq[:, fluid]) / tau # 体积力驱动采用外力项加权近似 for i in range(9): f[i, fluid] w[i] * Fx * ex[i] # 迁移周期边界 for i in range(9): f[i] np.roll(np.roll(f[i], ex[i], axis0), ey[i], axis1) # 反弹固体格点内交换反向分布 for i in range(1, 9): if i opp[i]: tmp f[i, solid].copy() f[i, solid] f[opp[i], solid] f[opp[i], solid] tmp if step % 1000 0: res np.abs(ux[fluid] - ux_prev[fluid]).mean() if res 1e-9: break ux_prev ux.copy()这里的碰撞项把分布函数按松弛时间拉向平衡态(\tau0.8) 对应运动粘度 (0.1) 格子单位粘性耗散适中。体积力项加在迁移之前外力会沿 x 方向驱动流体注意这是教学用的简化外力实现严格做法需要在平衡速度里加入 ( \tau F/\rho ) 的修正项孔隙率极低或速度梯度很大时两种写法会有可观测差异。迁移后分布函数会流入固体格点反弹段将固体格点内的分布函数成对反向交换等效于把进入固体的粒子弹回流体有效无滑移边界落在流体与固体格点之间。最后用流体格点的速度变化残差做稳态判据这个阈值要参考入口体积力和孔隙率来调外力越小收敛越慢但残差阈值不能跟着放松否则渗透率会被未稳定的慢速流动带偏。3.3 多孔介质 LBM 的关键参数表与量纲约定把格子单位换算到物理单位是新手最容易翻车的地方。LBM 计算里长度、时间和质量各有一个独立尺度渗透率换算公式为 ( k_{phys} k_{lb} \cdot (\Delta x_{phys}/\Delta t_{phys})^2 )其他量都由这三者推出来。下表给出我一般会用的初始化参数区间。参数经验取值影响松弛时间 tau0.55~0.9影响数值稳定性和有效粘度最小喉道宽度≥10 格决定反弹边界的误差占比孔隙雷诺数 Re_pore1最好 0.1保证达西流线性Knudsen 数0.001保证连续介质假设成立体积力 Fx1e-7~1e-5太小收敛慢太大产生惯性效应孔隙雷诺数用孔隙平均速度乘上孔喉特征长度再除以运动粘度在格子单位里可以直接算。如果 Re 大于 1惯性项不可忽略Darcy 定律的线性关系不再成立这时候再套渗透率公式会得到一个随驱动力变化的人造渗透率。Knudsen 数以分子自由程与孔喉直径之比估计格点单位下很难直接给出但可以记住一条经验tau 离 0.5 越远非连续效应越小tau 超过 1.2 后反而是强耗散掩盖了物理粘度。4. 从多孔介质 LBM 结果反推渗透率达西定律与五个稳定性坑4.1 达西定律的渗透率提取与单位换算稳态后的流场可以直接套达西定律算渗透率。对体积力驱动的周期结构宏观压力梯度就是 (-\Delta p/L Fx)体积平均速度取流体格点上的速度平均值公式是[ k_{lb} \frac{\nu_{lb} \cdot \langle u_x \rangle_{fluid}}{F_x} ]代码实现很简短# 稳态后的渗透率格子单位 nu_lb (tau - 0.5) / 3.0 u_avg ux[fluid].mean() # 孔隙内体积平均速度 k_lb nu_lb * u_avg / Fx # Fx 为体积力等效压力梯度 # 换算到物理单位 dx_phys 1e-6 # 每个格点对应 1 微米 dt_phys 1e-8 # 时间步由粘度匹配确定 k_phys k_lb * (dx_phys / dt_phys) ** 2 print(permeability (m^2):, k_phys)注意 ux[fluid].mean() 是流体格点速度的算术平均它没有乘孔隙率因为取平均的集合已经是流体域。若要当场验证孔隙率是否真的参与了流动可以对照经验公式 Kozeny-Carman( k \approx d_p^2 \phi^3 / (180(1-\phi)^2) )其中 (d_p) 是固体圆盘直径的物理尺寸。两者能对上量级就说明主循环没有系统性错误偏差超过两三倍时先检查喉道宽度是否足够再检查反弹边界是否把有效截面缩了。单位换算里(\Delta t_{phys}) 不是随便定的常见做法是先定 (\Delta x_{phys})再根据希望匹配的物理运动粘度反推时间步使得格子粘度和物理粘度满足同一个无量纲数。4.2 多孔介质 LBM 常见的五个坑与对应排查手段跑多孔介质 LBM 时渗透率结果不对的原因通常很集中。下面五类问题我基本每次都先排查一遍。现象原因对策渗透率随网格加密明显变化喉道格点数不足反弹边界偏差未消除喉道至少 10 格做 2~3 套加密验证速度场出现棋盘式振荡tau 距 0.5 太近数值耗散不足调到 0.7~0.9 再观察出口流量迟迟不收敛体积力太小低速区域残差被舍入误差淹没先跑 5000 步看收敛曲线再决定放大 Fx渗透率偏高或偏低但流场正常固体格点算入了速度统计或者反弹边界有效位置偏移统计时用 fluid 掩膜排查有效截面与 Fluent 等宏观 CFD 结果系统性偏差壁面位置、孔隙率不一致入口出口条件也不同用相同二值几何和二值化阈值进行对照最后一条在多孔介质里尤其隐蔽。格子玻尔兹曼的反弹边界有效位置在流体与固体格点之间等效于把固体圆盘的半径扩大 0.5 格宏观 CFD 的壁面严格落在几何边界上。小孔隙下这 0.5 格误差会显著改变渗透率。处理方法是在统计几何参数时把固体边界外扩 0.5 格作为实际流动边界或者用更精细的插值反弹边界格式来降低壁面位置误差。对多数工程判断来说先保证喉道分辨率够、再用两种尺度验证比花大力气改边界格式更划算。5. 进阶技巧用迂曲度复核多孔介质 LBM 的稳态结果5.1 迂曲度的格点算法渗透率是标量能描述整体阻力却无法暴露流场内部的细节错误。迂曲度tortuosity描述的是流体实际绕行路径与宏观直线距离的比值对骨架产生的流动异常敏感。稳态流场中迂曲度可以用速度矢量的分布直接估计[ T \frac{\langle |\mathbf{u}| \rangle_{fluid}}{\langle |u_x| \rangle_{fluid}} ]分子是速度模长的体积平均分母是轴向速度绝对值的体积平均。代码实现如下uxf ux[fluid] uyf uy[fluid] speed np.sqrt(uxf**2 uyf**2) tort speed.mean() / np.abs(uxf).mean() print(tortuosity:, tort)这段计算的核心是区分平均方向与平均路径长度。常见错误是用 (\sqrt{\langle u_x^2\rangle}) 做分母这会因对称流动中 (u_y) 的均值几乎为零结果把横向脉动当成迂曲度的一部分算出来的 T 系统性偏大。正确做法是保留绝对值。对随机圆盘骨架孔隙率 0.65 时迂曲度通常在 1.2~1.5 之间孔隙率越低T 越大如果算出来 T 小于 1.05多半是流场没有充分发展或体积力已经超过达西区间流线被拉直了。5.2 与 Fluent、COMSOL 的对照验证方法等渗透率和迂曲度都从 LBM 里出来建议再和宏观流体仿真做一次交叉验证。Fluent 和 COMSOL 都能导入同一张二值掩膜生成几何但这里有一个常被忽略的细节对照时不能只比渗透率。渗透率是一个积分量不同的局部流场分布可以凑出同一个值所以先把速度场导出来再算一次同样的迂曲度和沿主流方向的流线分布。COMSOL 里可以直接用速度分量构造自定义表达式Fluent 则在后处理里用 Custom Field Function 定义绝对速度和轴向速度两者计算迂曲度的口径要保持一致。LBM 速度场与 COMSOL 或 Fluent 的偏差维持在 5% 以内基本可以判定反弹边界和多孔骨架的设置没有问题偏差集中在某个局部区域时优先检查该区域的喉道分辨率和二值化阈值而不是怀疑求解器本身。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →