我没写一行代码,让 AI 把 PFC3D 接上了 OpenFOAM
PFC 自带的 CFD 模块,本身不含流场求解器。
它只干两件事:算孔隙率、算拖曳力,然后通过 socket 把数据递给外部求解器;压力、压力梯度、流速再传回来。真正的流场,得由第三方算。Itasca 官方给的方案是接 OpenFOAM,代码公开在 GitHub 上。
问题是那份代码停在 2022 年 1 月,要求的组合是 PFC 7.0 + Linux + OpenFOAM v2112 + Ubuntu 20.04。我这台机器是 PFC 6.0 + Windows + WSL2 Ubuntu 22.04,四条里错了三条。上游 README 自己写了:全部算例里只有两个能跑。
这个项目我给 AI 的全部输入是一句话——“把 PFC 和 OpenFOAM 耦合起来”。
后面的环境诊断、装 OpenFOAM、编译求解器、打通跨操作系统的通信、改脚本、验证、做冲刷算例,全是它自己做完的。整个过程我没写过一行代码。
一句话:这类工作的难点不在物理,在于"知道这个版本该怎么写、坑在哪"——而这类知识恰好可以固化进技能,交给 AI 执行。
这不是"跑个脚本"的事。我把 AI 实际走过的路径整理了一下,一共八步:
图 1 从零到跑通 CFD-DEM 耦合的八步。每一步对上一步的产物有强依赖,中间任何一步卡住后面都动不了。
八步里,只有第 2 步和第 5 步需要我在键盘上做点事,其余全是它自己走完的。这两处后面单说。
值得一提的是第 1 步——它没有直接开始装东西,而是先写了一个环境自检脚本,把"能不能做"拆成 8 个可验证的项:PFC 控制台在不在、CFD 模块加载没有、WSL 通不通、跨系统 socket 能不能真的收发、OpenFOAM 装没装、Python 包齐不齐。
这一步的价值在后面才显现:任何一个环节出问题,能立刻定位到是哪一层,而不是面对一个"跑不起来"的黑箱。
先把架构摆清楚:
图 2 耦合拓扑。PFC 在 Windows 侧当服务端,OpenFOAM 在 WSL2 里当客户端,靠 TCP socket 交换数据。
先说清楚 WSL2 是什么。
WSL2 全称 Windows Subsystem for Linux 2,是微软官方的"在 Windows 里跑 Linux"方案。
它和第一代最大的区别是:WSL2 跑的是真正的 Linux 内核——微软自己编译的,运行在一个轻量虚拟机里——而不是靠系统调用翻译。所以它是一个完整的 Linux:能 apt 装软件、能编译 C++、能跑 Docker。我们后面要装的 OpenFOAM、要编译的求解器,都是在这样一个真内核上跑的。
那为什么不干脆用 VMware 装一个 Ubuntu?
wsl -e bash -c '...' | ||
/mnt/d/... | ||
127.0.0.1 两侧透明 |
对这个项目来说,后两行是决定性的:整条链路要靠命令行编排(AI 才跑得起来),而跨系统的 socket 通信又要求网络尽量透明——这两条正好是 WSL2 的强项。
也不是说 WSL2 全面更好:它只能跑 Linux。要跑 Windows / BSD 这类别的 guest 系统、要做完整的快照与克隆、要更强的隔离,传统虚拟机(VMware、VirtualBox)仍然更合适。这个项目选它,是因为场景恰好对上了——纯 Linux 工具链 + 必须和 Windows 侧的 PFC 通信 + 全程命令行自动化,WSL2 在这三点上都占优。
上游的设计是两端都在 Linux,直接走 127.0.0.1 就完事。我这里是 PFC 在 Windows、OpenFOAM 在 WSL2——看着也是同一台机器,但 WSL2 默认的 NAT 网络模式下,WSL 里的 127.0.0.1 指的是它自己,不是 Windows 主机。
实测的现象很干脆:
第一反应通常是"那用主机 IP,开个防火墙端口"。AI 没走这条路,它翻了 WSL 的配置项,找到 镜像网络模式(mirrored networking):开启后 WSL 的网卡变成局域网里的真实地址,127.0.0.1 在两侧双向透明。
效果是:
耦合脚本一个字都不用改,也没有开任何防火墙入站端口。
这一处的意义:它没有停在"让代码能跑",而是选了改动面最小的那个方案。上游代码不动,意味着后面出问题时可以确定"不是我们改坏的"。
链路通了不代表结果对。耦合跑出来的数字,得先跟一个有理论解的问题对一遍。
选的是最经典的 Stokes 沉降:一颗 5 mm 的球,在粘性流体里自由下沉,终速有解析解。
图 3 上:耦合模拟的沉降速度曲线与 Stokes 理论值。下:实测终速与含壁面修正理论值的比值。
| 与壁面修正的吻合度 | 95.3% |
剩下的 5% 差异不是"没对上",而是粗网格法本身的特性:球半径 5 mm,流体网格 91 mm,球比网格小了 18 倍——这正是 CFD-DEM 粗网格法设计的场景,用它就是要接受这个量级的经验误差。
稳态判据也是硬的:末三步速度几乎不变(-4.194768e-02 / -4.194794e-02 / -4.194820e-02),受力不平衡量趋近于零。
验证过了,换真问题:水槽里堆一个碎石脊,用水流冲它。
水槽 0.50 m 长,脊体用 547 颗半径 8 mm 的碎石堆出来(密度 2650 kg/m³,接近真实岩石),流体是水。入口流速从 0 线性爬到 1.5 m/s,用时 2 秒,然后保持。
先看初始形态——颗粒床的形状是设计出来的,不是随便倒的:
图 4 上:水槽中跨剖面的颗粒分布与设计剖面。下:逐 1 cm 统计的床面高程与设计的对比。
颗粒床用 ball generate非重叠生成再重力沉降,不是 ball distribute 那种靠允许重叠凑孔隙率的路子。冲刷研究里孔喉几何是主角,床体本身失真了后面全白算。
然后是冲刷过程本身:
图 5 冲刷过程(中跨剖面切片,故画面中的颗粒数少于总数)。流场为底,颗粒叠在其上。
从流场切片能更清楚地看到脊体对水流的影响:
图 6 中跨剖面的流速矢量(颜色为流速大小)与颗粒床轮廓。水流越过脊体时明显加速。
定量看两端:
图 7 上:床面最高点与颗粒平均高度随时间的演变。下:脊体范围内(z > 0.05 m)的颗粒数。
图 8 上:颗粒最大速度随时间的变化。下:流场最大速度与入口流速爬升的对比。
结果定性是清楚的:颗粒速度从 0.05 一路涨到 1.15 m/s 并剧烈波动,脊体颗粒数先在 t≈1.2 s 降到 62(上游坡面被冲走),再升到 80(颗粒在下游沉积)。
第一轮其实没冲起来。
全程跑完,脊顶只降了 0.36 mm——而颗粒直径是 16 mm。t=1.5 s 之后颗粒就不再动了。换句话说,算例"成功跑完",但物理现象是错的(准确说:是流速没越过阈值)。
原因是 0.8 m/s 卡在临界值之下。这里有个容易踩的坑:如果按单个自由球的拖曳力去估,0.8 m/s 下的拖曳力约是它水下重力的 6.3 倍,看着早该冲动了。但床面颗粒与邻居互锁,起动条件远高于自由球。按床面颗粒的 Shields 判据,16 mm 颗粒对应的临界流速是 0.82–1.03 m/s。
把入口流速改到 1.5 m/s 之后,现象立刻出来了。
估临界流速:一律按床面颗粒(Shields)算,别按自由球拖曳力算。两者能差出一个数量级,而"跑完了但不动"这种结果,比报错难发现得多。
说"全程 AI 完成"不太诚实,更准确的说法是:极少数必须由人执行的动作之外,全是它做的。
我实际动手的只有三处:
其余时间我在看它自己排查问题。
举一个具体的例子。编译 pyDemFoam 时,编译和链接都不报错,但一 import 就抛:
它没有去改代码,而是去查了这个符号:库里的读函数是 read(int&),而这个报错要的是 read(long&)——是 OpenFOAM 的 label 位宽不一致。上游的构建脚本写死了 64 位,而本机装的是 32 位版本。
修法是让构建脚本去读环境变量 $WM_LABEL_SIZE 自动跟随,而不是写死。
这类判断才是关键:报错信息里没有任何一个词提示"位宽",它是从符号表反推出问题所在的。这种活人也能干,但很费时间;所以它是一个"知识密集但不创新"的问题——恰好适合交给 AI。
这件事的最后产物不是一个算例,而是一个可复用技能包。
它给出来的不是一段代码,是六项现成的能力:
| 环境自检 | |
| 环境搭建 | |
| 算例生成 | |
| 耦合模板 | |
| 后处理 | --case <目录> 调用 |
| 坑清单 | 15 类实机坑 |
意义在于:下一次要做一个新的冲刷算例,不需要重新发现上面这些事。 改几个参数生成算例,跑就完了。
想自己用的话,先看这几条前提
① WSL2 需要你自己提前装好(是什么、为什么不用 VMware,见第二节)。技能不负责装 WSL2——那是 Windows 的功能组件,要在"启用或关闭 Windows 功能"里勾选,或在管理员终端跑 wsl --install,然后重启。技能是从「WSL2 已经可用」这一步接手的,负责在里面装 OpenFOAM、装依赖、编译求解器。所以严格说,它不是"零前置",而是把 WSL2 之后的所有环节都自动化了。
② PFC 6.0 的 license 必须含 CFD 模块。这是硬门槛——没有它,cfdarray 这套接口根本加载不出来。自检脚本会验这一条:加载成功时日志开头应有 CFDModule3D loaded 和 CFDPython3D loaded。
③ Windows 11 22H2 以上。跨系统通信要用的镜像网络模式,是这个版本才引入的。
④ 留出几个 GB 磁盘(OpenFOAM 全套加编译产物)。
诚实声明
① 上游求解器是 Itasca 官方的 PFC3D_OpenFOAM(Jason Furtney,最后提交 2022-01-28),本工作是把它在本机环境跑通并封装,不是从零实现。其 C++ 源码一行未改。
② 该求解器是层流实现,无湍流模型,而真实水流 Re ≈ 2e5。所以流速增大→颗粒起动这个定性趋势成立,湍流相关的定量预测不成立。
③ 与解析解 95.3% 的吻合度是在 Re=0.378 的 Stokes 区取得的,不能外推到高 Re 工况。
④ 本方案仅适用于 PFC3D:PFC 的 CFD 模块没有二维版本,PFC2D 里 from itasca import cfdarray 直接抛 ImportError,二维走不通这条路。
VibePFC 社区 · 一起把 AI 玩进 PFC
我成立了一个 VibePFC 社群,专门聊 AI 怎么落地到 PFC / 数值模拟——让大模型读懂模型、自己改脚本、自动排查“看不懂”的问题。收一个 50 元的门槛费。
想进群的同学,加 QQ 763388012,备注“vibePFC”即可。
PFC3D_OpenFOAM(该方向唯一的正规实现)~/.claude/skills/pfc-openfoam/(含环境自检、算例生成器、耦合模板、15 类坑清单)openfoamOuhe/dropTest1/、openfoamOuhe/scour/