多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

COMSOL四场耦合动态渗透率建模:瓦斯抽采数值模拟实战

COMSOL四场耦合动态渗透率建模:瓦斯抽采数值模拟实战 做瓦斯抽采数值模拟的人看到 Comsol 模拟仿真和四场耦合这两个词应该立刻会想到热-流-固耦合下的动态渗透率问题。我最初做这类项目时也踩过不少坑发现很多人拿到煤层瓦斯抽采的课题习惯性先建一个达西渗流模型给一个孔隙压力、给一个渗透率然后跑出一条压降漏斗和流量曲线。结果放到工程上预测流量和实际矿井测点对不上误差常常不是百分之几十而是几倍。问题往往就出在渗透率被当成常数。瓦斯抽采过程里煤体并不是刚性骨架。钻孔周围应力重分布煤体发生压缩或卸荷瓦斯解吸后煤基质收缩、裂隙开度改变温度随解吸吸热下降又会反过来影响基质应变。这些变化都会导致渗透率在时空中持续变化。如果只做单场渗流模拟等于把所有骨架响应都砍掉了模型再精细也只是在给定渗透率条件下做“管道计算”自然不是真实的抽采过程。所以我在后续项目里果断把应力场、渗流场、温度场和孔隙度/损伤演化放在一起建模用动态渗透率把四个场串起来。下面这篇文章以我做过的一个钻孔抽采模型为例从方程搭法、COMSOL 具体操作、求解器调试到批量参数化扫描把整套流程写清楚。适合正在做 COMSOL 多场耦合建模的研究生、工程师也适合想从单场模拟往四场耦合进阶的同行参考。1. 四场耦合不是把四个物理场堆在一起先理清逻辑1.1 单场渗流模拟为什么会在工程预估中翻车很多人把渗透率当成常数来处理这是工程误差的主要来源。钻孔抽采时煤体应力会重新分布钻孔周围可能出现卸压区、应力集中区煤体骨架的压缩或膨胀直接改变孔裂隙尺寸。瓦斯解吸以后煤基质会发生收缩裂隙开度增大渗透率上升但在某些高应力区域煤体被压缩裂隙闭合渗透率又会下降。这些机制都是和应力场绑定的。如果模型里没有变形场渗透率永远是初始值抽采后期预测的流量就会明显偏高或偏低。温度场也是一样的道理。瓦斯解吸是吸热过程煤体温度会下降温度变化影响煤基质热应变热应变又改变裂隙宽度。可以说动态渗透率不是一个可选的“高级功能”而是瓦斯抽采数值模拟里绕不开的核心环节。单场模型不是不可以用但它只适合初步估算不能用来做工程方案比选。1.2 这个模型里的四场分别指什么很多人会问“热-流-固”明明只有三个场哪来的四场实际工程模拟里第四个场往往是孔隙度/损伤演化场或者浓度场取决于你关注的是瓦斯解吸、运移还是围岩破坏。标题里“动态渗透率与孔隙……”的后半句其实就是关键动态渗透率的本质是孔隙度和裂隙开度在应力、温度、压力作用下的动态响应。因此我这里的四场定义为固体变形场、瓦斯渗流场、温度场、孔隙度/损伤变量场。第一个场是固体变形场控制方程是静力平衡或准静态平衡方程输出位移、应力、应变提供有效应力状态。第二个场是渗流场基于达西方程输出瓦斯压力分布和流速。第三个是温度场考虑热传导、对流换热和吸附/解吸热效应。第四个是内变量场用孔隙度或损伤变量刻画介质结构的演化。四场之间不是简单地“两两耦合”而是存在多个反馈回路有效应力影响孔隙度和渗透率渗透率反过来决定压力传播速度压力改变又改变有效应力温度影响热应变和吸附应变应变又改变孔裂隙形态。COMSOL 中把这些反馈写成明确的表达式就能得到动态渗透率的完整闭环。1.3 为什么选 COMSOL 而不是自己写有限元程序遇到这种强非线性耦合问题有人会说“干脆自己写有限元”我以前也这么想但现实很骨感。四场耦合涉及多个偏微分方程还要处理动态渗透率这种跨数量级的非线性系数自己写程序光雅可比矩阵和单元组装就够忙很久更别说后处理了。COMSOL 的优势是物理接口开发得比较成熟固体力学、达西定律、多孔介质传热都有现成模块COMSOL 6.4 这类新版本还增强了自动网格和求解器默认策略。我更看重的是它的“变量节点”和“组件耦合定义”可以把动态渗透率表达式一次性写好让所有物理接口共用计算时自然读入当前应力、温度、孔隙度省去大量手写耦合代码。2. 控制方程和动态渗透率我在 COMSOL 里是这么搭的2.1 四场控制方程的最小集合先把最小方程集合摆出来。固体变形场我常用的形式是[ abla \cdot \sigma_{eff} F 0 ]其中 (\sigma_{eff} \sigma_{total} - \alpha p I)(\alpha) 是 Biot 系数(p) 是瓦斯孔隙压力。本构写成[ \sigma D : (\varepsilon - \varepsilon_0 - \varepsilon_T - \varepsilon_s) ](\varepsilon_T) 是热应变(\varepsilon_s) 是吸附/解吸引起的基质应变。注意吸附和解吸的符号方向相反解吸时基质收缩等价于体积应变减小。渗流场用质量守恒加达西定律[ \frac{\partial (\rho \phi)}{\partial t} abla \cdot (\rho u) Q_m ][ u -\frac{k}{\mu}( abla p \rho g abla z) ]瓦斯密度不能当常数低压下按理想气体 (\rho pM/(RT))压力高时要加压缩因子 (Z)。有些文献直接引入“气体含量”而不是密度重点关注质量守恒而非体积守恒。这一点在 COMSOL 里尤其重要因为如果采用体积形式的达西方程单位逸度和饱和度会搞混。温度场用多孔介质能量方程[ (\rho C_p){eff} \frac{\partial T}{\partial t} (\rho C_p)f u \cdot abla T abla \cdot (\lambda{eff} abla T) Q{ads} ]这里的 (Q_{ads}) 是吸附/解吸热源项瓦斯解吸吸热所以通常取负号。最后是孔隙度/损伤演化场最简单的做法是设一个内变量 (\phi)并给定演化方程 (d\phi/dt f(应力、温度、气体压力))或者用一个 ODE 定义损伤变量 (D)再通过 (D) 映射到孔隙度和渗透率。COMSOL 里这个场不需要边界条件它是一个分布在整个域的内变量。2.2 动态渗透率模型指数型还是 Kozeny-Carman 型动态渗透率模型是整个模型的灵魂。我见过的做法大概三类指数型(k k_0 \exp[-A(\sigma_{eff} - \sigma_{eff,0})])适合裂隙主导介质参数 (A) 需要实验标定。它的好处是渗透率随有效应力单调下降工程上最容易理解。Kozeny-Carman 型(k k_0 (\phi/\phi_0)^3 ((1-\phi_0)/(1-\phi))^2)适合孔隙主导介质只依赖孔隙度变化表达式更“物理”但煤体里裂隙占主导时孔隙度变化对渗透率的影响远不如裂隙开度大。裂隙立方型(k k_0 (1 \Delta b/b_0)^3)(\Delta b) 是裂隙开度变化适合含宏观裂隙的模型。实际建模时我通常把它们组合使用孔隙度变量负责孔隙部分损伤变量负责裂隙开度部分最终渗透率写成 (k k_{por}(\phi) k_{frac}(D))。但要注意动态渗透率不是直接在 COMSOL 里写一个复杂的 if 条件而是用中间变量一步步算。一般是先算当前有效应力和热应变再算孔隙度与损伤最后组合出渗透率。写表达式时用到了平滑函数和上下限截断后面我会讲为什么。更关键的工程问题是标定。动态渗透率模型里的 (A)、(C)、Kozeny-Carman 系数不是随便从文献抄一个就完事。不同矿区煤体的裂隙密度、吸附特性差别很大。我用过最稳妥的方法是做 2 到 3 组有效应力-渗透率实验再拿矿井实测抽采流量做反向校核。如果只有一组实验数据那就把模型定位为“机制研究敏感性分析”不要强行给现场预测下结论。2.3 物理接口和耦合变量的设置建议COMSOL 的接口选择我建议用“固体力学 达西定律 多孔介质传热 系数型 PDE/ODE”而不是一个接口包打天下。固体力学接口输出位移和应力达西定律接口输出孔隙压力多孔介质传热接口输出温度内变量场用“域上的常微分方程”或“系数形式 PDE”来描述。四场耦合的核心不是把四个接口都摆上就算完而是在“定义”节点里写清楚变量之间的映射关系。比如我会在组件下建一个 Variables 节点集中定义平均有效应力sigma_m_eff (sx sy sz)/3 - alpha*p孔隙度phi phi0 dphi_se渗透率k k_por k_frac然后把这些变量填到达西接口的 permeability 表达式同时在固体力学里把孔隙压力加为载荷在传热接口里把解吸热加为源项。这样物理接口之间的耦合关系一目了然。COMSOL 的变量名有默认规则比如固体力学接口的压力变量可能是p传热接口是T具体以你版本里的“方程视图”为准。为了避免写错我习惯把所有中间量都放在自己的变量节点里并加单位检查这一点对于四场耦合模型尤为重要。提示在 COMSOL 六点几版本里一个物理接口的变量前缀可能随版本变化。不要依赖记忆写表达式打开“变量”表和“方程视图”核对单位能把收敛问题消灭掉一半。3. COMSOL 建模实操从几何、网格到求解器3.1 几何模型先用二维轴对称把问题跑通这类模型建议先用二维轴对称几何不要一上来建三维。以钻孔抽采为例我习惯做一个半径 10 米、长度 20 米的煤体域钻孔半径 0.05 到 0.1 米位于对称轴处。几何非常简单但物理过程已经足够复杂。三维模型留给后期验证时用。网格方面钻孔壁附近压力梯度最大渗透率演化最剧烈必须加密。我通常用边界层网格在钻孔壁布置 5 到 8 层第一层厚度取钻孔半径的 1/50 到 1/100然后向煤体内部渐变。远离钻孔的地方用较粗的规则网格。单元质量检查重点看偏斜度大部分单元要在 0.3 以上。如果你开了移动网格要特别注意网格扭曲。但我必须提醒瓦斯抽采变形通常是厘米级甚至更小不一定需要移动网格除非你要模拟裂隙显著张开或闭合、钻孔大变形这类几何拓扑变化明显的问题。用移动网格会显著增加计算量和收敛难度能不启用就不启用。3.2 参数、单位和变量命名最容易翻车的坑四场耦合模型的参数非常多单位问题是我见过最多翻车点。列一张典型参数表给新手直接参考参数符号典型取值单位说明初始渗透率k01e-15m²约 1 mD煤体范围可以很大初始孔隙度phi00.051不是 5%是 0.05弹性模量E2.5GPaCOMSOL 里写 2.5[GPa]内部会转 Pa泊松比nu0.31无因次瓦斯动力黏度mu1.1e-5Pa·s甲烷常温近似初始瓦斯压力p01.5MPa用绝对压力写 1.5[MPa]钻孔抽采压力pb0.08MPa表压很难写建议用绝对压力Biot 系数alpha0.61取决于煤体热扩散系数a1e-6m²/s用于估计时间尺度这里最典型的坑是渗透率。工程资料里瓦斯渗透率经常给 mD1 mD 9.87e-16 m²接近 1e-15。如果你忘记换算整个模型压力传播速度会差 3 个数量级结果完全不能用。其次压力基准统一用绝对压力就不要在某个边界突然写一个负的表压和大气压混在一起。变量命名方面我建议所有中间变量都放在 Variables 节点里统一管理名字用可读性强的sigma_m_eff、phi、k_dyn这种而不是默认的p、T到处漂。COMSOL 的单位检查会提示报错但前提是你写得足够规范否则它会直接给你一个数值灾难。3.3 边界条件与初始条件怎么给以二维轴对称钻孔模型为例。渗流场钻孔壁给定压力 pb外边界和上下边界默认零通量初始值 p0 全域。固体场模型外边界固定法向位移或施加地应力对称轴处用对称边界钻孔壁是自由边界。由于孔隙压力变化会产生有效应力改变固体力学里必须把 p 加到载荷项上否则变形场会和渗流场脱节。温度场钻孔壁可以是固定温度或对流换热远处边界绝热初始温度 T0 按原岩温度给。损伤/孔隙度场初始值设 phi0 或 D0一般不需要边界条件。初始条件最重要的是和边界条件一致。如果你设置 p01.5 MPa钻孔壁却给 0.08 MPa那就已经是一个强非线性的初始跳跃。求解器会尝试处理但你最好先做一个稳态的渗流-变形耦合再以这个稳态结果作为瞬态初始值能明显减少冷启动困难。实际操作中我会在“研究”里先跑一个稳态辅助研究再把稳态结果作为瞬态的初始值。COMSOL 的“辅助扫描”和“存储解”都能做这件事。这个小流程是四场耦合建模里最值得养成的好习惯。3.4 求解器设置与收敛控制接下来控制迭代收敛。先用全耦合牛顿法跑一个粗网格、短时段的模型确认物理趋势正确再扩展到精细网格和完整时间范围。全耦合牛顿法在模型规模不大时最容易理解但四场耦合模型自由度一旦上到几十万直接全耦合会非常吃力。求解器方面COMSOL 默认会自动选择但四场耦合我一般手动改成分离式求解器把变量分成三个组第一组是孔隙压力和温度第二组是位移第三组是内变量损伤/孔隙度。分离式求解的思路很像“迭代耦合”每个组的方程规模更小内存占用低也更容易诊断哪个场发散。每组内部的线性迭代用默认组间的阻尼因子从 0.7 到 0.9 开始尝试如果残差不降就往下调。对于时间项BDF 时间积分里的最大阶次设 2 足够时间步长交给自适应但可以限制初始步长不要太大。还有若介质压缩性很强压力波传播快瞬态求解可能需要隐式求解器里开启“J 更新每个牛顿迭代”否则雅可比矩阵滞后会产生锯齿状振荡。4. 跑模型时最容易踩的坑和排查记录4.1 孔隙压力出现负值先检查密度和压力基准这个坑很经典。用达西接口时如果瓦斯密度写成理想气体 (\rho pM/(RT))参数又用了绝对压力那压力在物理上不会变负。但很多人把压力写成了表压初始表压 1.4 MPa、钻孔 0 MPa在瞬态求解时数值振荡可能越过零点密度成了负数孔隙压力就会往下冲到不物理的负值。我的处理是全局统一绝对压力在气体密度表达式里加保护比如用max(p, p_limit)或者用 COMSOL 的flc2hs平滑函数把压力限制在极小正值附近。这样即使中途迭代发生过冲也不会让密度、黏度这些物性直接爆炸。另外动态渗透率里如果用了压力相关的表达式也要同步保护否则负压会串到渗透率里产生更大的振荡。4.2 动态渗透率突变导致数值振荡限幅和平滑比截断更有效动态渗透率模型一旦包含指数项渗透率在局部区域可能在几个时间步里跨两三个数量级。这种情况下求解器会很难受表现为压力不收敛、流量曲线锯齿状。不要简单用if(p0, k_high, k_low)这种硬截断因为硬截断让导数不连续雅可比矩阵无法准确更新。更合理的做法是用平滑函数或连续函数压缩变化范围[ k k_{min} (k_{max} - k_{min}) \cdot smooth\left(\frac{x - x_0}{scale}\right) ]COMSOL 里有flc2hs和flsmhs等平滑 Heaviside 函数配合分段参数控制变化剧烈程度既能保持渗透率变化趋势又避免数值跳跃。另一个经验是把渗透率的更新频率和压力时间步解耦先在较小时间步内固定渗透率得到压力场初步收敛再开启渗透率更新。这个在分离式求解器里对应“顺序更新”或“阻尼更新”只要不是一上来就全耦合硬算大多能跑下来。4.3 温度场和流场时间尺度差异太大全耦合算不动怎么办瓦斯抽采中的规律往往很麻烦渗流压力在数天到数十天尺度传播而温度扩散在更长时间尺度上更慢吸附解吸热效应又要更长时间才能体现。全耦合瞬态计算会为了捕捉最快的压力波把时间步压得极小温度场却几乎没变化白算很多步。如果只想研究渗透率和产气量的演化可以先解开渗流-变形-损伤耦合把温度场简化为常数或稳态分布再做敏感性分析。如果一定要完整考虑温度影响建议用分离式求解器把温度场单独放到一个变量组并给温度组更大的时间步或者在每一时间步内先解温度做固定点迭代。很多时候我们真正关心的温度影响并不是热传导而是吸附解吸热导致的温差这个源项需要流体压力变化来触发所以“渗流算一阵更新温度源项一把再渗流”的弱耦合策略工程上反而更实用。用损耗更小的方式来换物理一致性比强行全耦合更明智。4.4 网格依赖性和不收敛问题速查表四场耦合模型结果对网格很敏感尤其是损伤局部化和渗透率突变区域。如果你的钻孔周围渗透率异常高或异常低先别急着调物理参数先做一次网格收敛性检验粗、中、细三套网格对比钻孔流量、压力漏斗形态、渗透率最值。主要指标变化小于 5% 再进入参数研究。如果不同网格结果差异很大那不是物理模型问题是网格分辨率不足或单元畸变。现象常见原因处理建议压力震荡发毛密度保护缺失或压力基准混乱统一绝对压力加 min/max 保护位移数值异常大孔隙压力载荷符号错误检查有效应力公式的符号确认载荷方向渗透率阶跃硬 if 截断或指数项剧烈用平滑函数限幅缩小变化范围网格收敛差损伤区单元宽度不够加密局部网格并做三套网格对比内存耗尽三维模型网格过密先用二维轴对称降阶后再验证瞬态时间步滚不下去初始条件与边界跳跃大先跑稳态辅助研究作为初始值这张表是我自己项目里的排查顺序按出现概率排列。遇到问题不要一次调三个参数那不是调模型是碰运气。5. 参数化扫描和 Python 批量控制5.1 为什么要批量跑参数单一模型只能给出一种条件下的算例工程上远远不够。你可能需要回答“抽采负压提高多少渗透率变化有多大”“钻孔间距多少最合适”“温度变化对流量影响多大”这些都要做参数化扫描。COMSOL 桌面端的“参数化扫描”简单模型很好用但四场耦合单次求解很耗时几十组参数下来人盯在屏幕前纯属浪费。我一般先把所有工况写进一个文本文件然后用后台批量求解最后统一提取关键结果。这样能显著提高效率。COMSOL 6.4 的批处理和 Session 功能比旧版本更好用如果还没有用过建议把官网文档里的 Batch Sweep 和 Job Sequence 看一遍能省很多手动点“计算”的时间。5.2 用 Python 远程控制 COMSOL 的一般流程Python 控制 COMSOL 的热度这两年明显上来了。常规方式有两种一是用 COMSOL 官方提供的二次开发接口在命令行启动后台服务二是用开源库 MPh 连接 COMSOL Server。我这边更常用后者做流程自动化思路是启动一个后台服务加载已建好的模型改参数求解导出结果最后关闭客户端。下面代码只是思路演示API 名称以你所用版本和库文档为准但流程是通用的import mph client mph.start(cores4) model client.load(gas_drainage.mph) # 修改全局参数例如钻孔压力和初始渗透率 model.parameter(p_borehole, 0.08[MPa]) model.parameter(k0, 5e-16[m^2]) model.solve() model.save() client.disconnect()批量循环也很直接把参数组合写成一个列表循环里改值、求解、保存结果文件。COMSOL 模型一旦在后台跑起来就不再占用前端图形界面你可以同时做后处理分析或写报告。如果公司用的是正版浮动许可证注意计算节点数量限制不要开太多并发否则许可证会抢占。用 Python 控制的最大好处是把整个仿真链路变成可复现脚本参数改了重跑一遍就行这也方便和实验数据联合分析。5.3 版本兼容性和新版本建议四场耦合模型往往好几年一直用换版本最怕的是物理接口名称和默认求解器策略变化。COMSOL 6.4 在界面布局和我之前用的 6.0 基本一致但 6.4 对自动网格处理和求解器默认策略做了调整有时打开旧模型会提示某些表达式被重命名。我建议升级后先把旧模型在 6.4 里跑一次粗网格验证不要直接拿来生产。安装 COMSOL 时记得把多孔介质流动、结构力学和传热模块选上否则后面想加物理场会发现接口缺失。我个人的习惯是在 6.4 里建新模型时把所有耦合表达式全部重新过一遍单位检查尤其是渗透率、黏度、压力这些跨物理场的变量。版本升级不是简单的“打开旧文件就行”花半天时间做回归验证后面能省一周。6. 最后再聊几点实战体会做这类四场耦合模型我最大的体会是别把动态渗透率当成一个听上去高大上的名字它就是一条连接所有物理场的数据总线。只要渗透率模型和实验对不上其他场算得再细都会失真。所以在做 COMSOL 仿真前先把物理机制和可标定参数想清楚比折腾网格和求解器重要得多。另一个体会是模型复杂程度要和数据支撑程度匹配。如果手里只有渗透率常数就别硬上四场耦合做一个三步走先渗流单场再渗流-应力二场最后才加温度和损伤。每一步都要和现场实测流量或实验室数据对账对不上就回头查参数。最后分享一个小技巧我在成果输出时总是同时导出钻孔壁附近的渗透率演化曲线和瓦斯流量曲线这两条线的对应关系最能说明动态渗透率的意义也是工程汇报里最有说服力的图。四场耦合模型跑通以后这套思路完全能迁移到地热开发、页岩气开采、注热强化瓦斯抽采等相近问题上。
返回列表