BASILISK开源CFD求解器:自适应网格与VOF多相流仿真实战

发布时间:2026/10/16 23:17:24
BASILISK开源CFD求解器:自适应网格与VOF多相流仿真实战
简介BASILISK是一款面向蛋白质结构研究领域的开源软件用于对氨基酸侧链构象进行连续概率建模弥补传统旋转异构体库方法对χ角空间离散化带来的精度损失适合生物信息学、计算化学方向的科研人员及具备C/Python基础的程序员使用。本次提供的源码包共27个文件压缩后仅72KB其中以24个Python脚本为主体涵盖侧链角度计算、概率模型训练、骨架依赖的χ角分布估计等核心模块同时包含INSTALL安装说明与LICENSE开源许可文件方便快速搭建环境。已有307人学习下载。通过这份代码用户可了解从PDB结构数据到侧链构象概率模型的完整实现流程并基于开源许可自行修改与扩展为蛋白质结构预测、稳定性分析等任务提供可定制的计算工具。1. BASILISK 是什么一个用 C 语言写流体力学的开源求解器为什么值得为它熬三个晚上拿到 BASILISK 源码那一刻多数人和我一样会愣一下这个开源流体力学项目居然没有 configure也没有 CMake官方只让你导出两个环境变量然后去编译一个 .c 文件。它不是一个装完点“下一步”就有的黑匣子而是一个以四叉树/八叉树自适应网格AMR为核心的求解器家族主要解决不可压缩流、气液两相流和自由表面问题。液滴、溃坝、气泡、波浪都是它擅长的场景。如果你预算有限又想用分辨率可调的多相流仿真BASILISK 值得投入但要接受它的学习曲线半小时能跑通 demo三天才能写出能加到报告里的算例。2. 先把底子打对自适应网格、VOF 和 BASILISK 的模块设计逻辑2.1 自适应网格为什么值得为它翻出老代码做 CFD 的人都知道网格决定了成本和精度。均匀网格最大的浪费在于你把大量格子花在了流场没有剧烈变化的地方而真正需要解析的边界层、液面、激波反而常常分辨率不足。BASILISK 的自适应网格用四叉树二维或八叉树三维组织计算单元界面附近自动加密远处的区域自动变粗每一步都可以调整一次。这意味着一个 64 × 64 的基网格配上调到 3 级细化局部能抽出等效 512 × 512 的细节但总格子数只有它的几十分之一。我自己第一次跑溃坝算例时最直观的感受是网格不是均匀铺开而是追着水气界面走。等界面移动到新的区域旧区域的格子自动回收内存和计算时间都在实时变动。BASILISK 做这一步用的是网格父子关系和平衡约束一个父格子最多裂成四个子格子粗化前必须满足最大等级差限制。这套机制稳定高效代价是你得接受它“网格会变”这件事——后处理时不能假设网格是固定拓扑。2.2 VOF 方法在 BASILISK 里的存在形式多相流里常见的界面处理方法有 Level Set 和 VOF流体体积法。BASILISK 主推 VOF核心是一个体积分数场 ff 1 表示该格子全是重流体f 0 表示全是轻流体0 f 1 的格子则包含界面。VOF 方法天然守恒体积分数在网格间对流时不会凭空减少配合 AMR 时只需要在界面附近加密算出来的液滴体积能保持得很好。BASILISK 把 VOF 拆成两部分vof.h 负责几何对流和界面重构tension.h 负责表面张力。表面张力计算依赖曲率而曲率又依赖界面附近网格的几何质量这一步是很多新翻车点。f 场只是一个标量但你在方程里需要访问它的梯度、曲率和法向分量时必须保证 f 所在网格足够细否则曲率误差会直接转成虚假的速度场也就是常说的 parasitic currents。网格自适应策略和 VOF 几何重构是绑定的不能只调一个。2.3 模块即头文件qcc、.h 和用户事件的关系BASILISK 没有传统意义上的库文件。你下载的源码里是一堆带复杂宏和嵌套回调的头文件用户写一个 .c 文件用 include 把需要的模块引进来再用官方提供的 qcc 脚本把这个文件编译成可执行程序。qcc 本质上是一个编译器前端它会先处理头文件里的宏和事件注册再把生成好的 C 代码交给系统 gcc。换句话说你的 main.c 不只是一个入口它更像一个“组装清单”。你可以自己动手验证这个结构打开源码里的 examples 目录任意挑一个 .c 文件查看它 include 了哪些头文件再顺着头文件去看它引用了哪些别的头文件。常见组合是 grid/multigrid.h 加 navier-stokes/centered.h 加 two-phase.h 加 tension.h。名字看上去像教科书目录实际含义分别是网格、不可压缩 N-S 求解器、两相流材料参数、表面张力。这种“按头文件选功能”的做法让 BASILISK 支持任意二维三维切换代价是学习时必须把模块边界记住。3. 本地编译与最小案例qcc 环境变量、依赖和第一个溃坝模拟3.1 装依赖与下载源码没有 configure但你需要备好这些系统包BASILISK 官方发布的是一个源码压缩包解压就能用但编译前需要几个系统依赖GSL数值计算、FFTW快速傅里叶变换用于求解压力泊松方程、MPI并行跑多核。Debian/Ubuntu 系可以直接用包管理器一次装齐sudo apt install gcc make libgsl-dev libfftw3-dev \ openmpi-bin libopenmpi-dev装完之后确认 gcc 能编译一个最简单的 C 程序比如printf(hi)再开始折腾 BASILISK。FFTW 和 GSL 的缺失通常不会在 configure 阶段报错而是在编译你的算例时出现找不到头文件的错误。所以如果你发现/usr/include/fftw3.h不存在先回来补包再继续。3.2 设置 BASILISK 环境变量编译找不到头文件的第一个坑BASILISK 最关键的安装步骤是两个环境变量BASILISK指向源码根目录PATH要包含该目录这样你才能在任意工作目录里调用 qcc。export BASILISK$HOME/src/basilisk export PATH$BASILISK:$PATH把这两行追加到~/.bashrc末尾下次开终端就不用再敲。这里有两个容易忽略的细节第一BASILISK路径里不要有空格或中文qcc 对路径的处理非常朴素第二PATH必须放在前面避免系统里存在同名工具时被抢先调用。设置完成后在终端执行which qcc能看到完整路径就说明环境变量生效了。3.3 写一个最小 main.c溃坝流的初始化、重力与自适应环境变量就绪后我们来写一个能直接跑的二维溃坝算例。场景是一个 1 × 1 的方形区域左边半个区域初始是水右边是空气之后靠重力倒下来。这是 BASILISK 里最常见的入门示例也最能体现 AMR 在界面附近加密的效果。#include grid/multigrid.h #include navier-stokes/centered.h #include two-phase.h #include tension.h #define LEVEL 6 int main() { origin (0., 0.); size (1.); init_grid (1 LEVEL); rho1 1., rho2 1e-3; mu1 1e-2, mu2 1e-3; f.sigma 1.; run(); } event init (t 0) { fraction (f, 0.5 - x); } event acceleration (i) { face vector av a; foreach_face(y) av.y[] - 1.; } event adapt (i) { adapt (f, maxlevel LEVEL 2, minlevel 4); }这段代码的核心逻辑拆开看grid/multigrid.h给出最常用的静态网格基础navier-stokes/centered.h提供二阶不可压缩流求解器two-phase.h负责定义两相密度和粘度tension.h加上表面张力。init_grid (1 LEVEL)表示基础网格是 2 的 6 次方也就是 64 × 64后面自适应最多加密到 8 级。fraction (f, 0.5 - x)用几何函数在 x 0.5 的区域初始化水。acceleration事件里的av.y[] - 1.给整个流场施加一个向下的单位重力加速度这是 BASILISK 里写入体积力最直接的方式。adapt事件在每个时间步根据体积分数场的误差加密或粗化网格minlevel 是粗化的下限maxlevel 是加密的上限。3.4 用 qcc 编译并运行输出日志的第一组指标看什么把上面的代码保存为dam_break.c然后编译运行cd $HOME/run01 cp $HOME/src/basilisk/examples/dam_break.c . qcc -O2 -Wall -o dam_break dam_break.c -lm ./dam_breakqcc 会把头文件里的宏展开并生成一个临时主程序再交给系统 gcc 编译。-lm是链接数学库漏掉它常常出现疑惑的 undefined reference 报错-Wall能帮你看到关于未初始化变量和类型转换的警告建议先开着。运行后终端会开始刷新日志t是当前时间dt是求解器自动计算的时间步长inc是牛顿迭代步数后面对应的是压力求解器和粘性求解器的迭代次数。如果你看到dt在不同量级之间变化说明自适应网格或表面张力正在影响时间步这本身就是 BASILISK 在正常工作的信号。4. 改参数跑通你的场景分辨率、自适应阈值和表面张力的三个必调点4.1 LEVEL决定整个方案成本的第一个数字LEVEL 是 BASILISK 里出现频率最高的参数。它表示基础网格的等级实际格子数是 2 的 LEVEL 次方。把 LEVEL 从 6 改成 7基础网格从 64 × 64 变成 128 × 128内存会翻倍计算量大致翻倍但界面几何和表面张力计算会明显更平滑。三维算例更夸张每加一级存储开销约 8 倍因为八叉树多了一整个维度。我一般建议先用 LEVEL 5 或 6 跑通流程确认物理结果大致合理后再逐步升到 7 或 8。工程上真正重要的是“让自适应网格在界面处达到目标等级”而不是整体提升基网格。你可以把minlevel固定为 4只调maxlevel这样远场仍然很粗成本能压得住。判断 LEVEL 是否够用最直接的办法是导出一张界面附近的网格图看水气交界面是否由至少 8 到 10 层网格覆盖。4.2 adapt() 与阈值自适应网格的“玄学”参数很多人第一次用 BASILISK打开文档看到 adapt 函数就懵了因为它的参数不是固定的而是可以同时传多个场。下面的写法是工程上最常见的event adapt (i) { adapt (f, u, maxlevel LEVEL 2, minlevel 4); }这里把体积分数场 f 和速度场 u 都作为自适应指标传进去。f 负责保证界面附近加密u 负责让流场里的涡和射流也能得到足够分辨率。两个变量可以同时传BASILISK 会对每个候选格子分别做误差估计只要其中一个场超过阈值就加密。误差的默认阈值对新手来说偏大我一般会让它比默认值更严格一点比如对 f 给 1e-3 量级对速度给 1e-2 量级具体数值可以通过在 adapt 里指定 error 列表来控制。这里有个容易踩的误区adapt 不是执行一次就完事的。界面一直在动如果你只在第 0 步调用一次后面的网格就不会跟随界面走。所以要把它放进每步执行的 event 里。调参时先看三个东西运行日志里的格子总数、dt 的变化幅度、导出的界面动画是否光滑。如果格子总数来回剧烈跳动多半是阈值设太紧网格在跟随噪声加密如果界面明显糊成台阶则是 maxlevel 不够或阈值太松。4.3 sigma 与粘度表面张力为什么是高翻车率参数表面张力在 BASILISK 里通过 f.sigma 设置。它不是一个独立可以随便填的量在一个无量纲化的问题里sigma、密度、粘度和特征尺度共同决定了毛细时间尺度。表面张力会把压力场推成界面两侧的跳跃跳跃的大小由曲率乘以 sigma 决定。当界面曲率很大比如液滴即将破裂的细颈处局部需要很细的网格去解析否则曲率算不准压力跳跃也错。sigma 设太大时时间步长会被表面张力约束压下来。原因是显式表面张力的稳定条件近似与 sigma 和网格尺寸的三次方相关网格越细允许的时间步越小运行速度会突然掉一个量级。遇到这种情况先确认你的 sigma 是否真需要那么大如果只是为了看现象适当调大粘度来稳定计算是一种工程妥协。粘度方面mu1/mu2 是运动粘度还是动力粘度取决于 two-phase.h 的具体版本和单位定义建议先在你自己跑的例子里打印一次原始数据做量纲核对避免最后结果差好几个数量级。5. 避坑与排查编译报错、切网格翻车和大规模计算的五个常见问题5.1 报了 undefined reference 却能编译过链接阶段丢 -lm 与模块顺序现象用 qcc 编译你的 .c 文件前面的语法检查没报错最后却冒出一堆undefined reference to exp、undefined reference to fftw_之类的信息。原因这不是源码写错而是链接阶段没找到数学库或 FFTW 库。BASILISK 的头文件里大量调用了数学函数-lm必须放在编译命令的最后某些版本里没有-lm会直接失败。解决把编译命令改成qcc -O2 -o dam_break dam_break.c -lm。如果 FFTW 不是在系统默认路径安装的还需要用-L指定库路径或者通过包管理器重新安装 libfftw3-dev。先检查/usr/lib下有没有libfftw3文件再处理链接参数。5.2 一运行就 Segmentation fault先查 f.sigma 和指针现象代码能编译但./dam_break一跑就崩终端直接打印Segmentation fault。原因常见于 f 场还没有被初始化就在事件里访问 f.sigma或者你在主函数里写了f.sigma 1.但忘记 include tension.h。另一种情况是自定义 event 里用了foreach_face但没有先给 face vector 赋值导致空指针。解决先用最简单配置跑通把 tension.h 先注释掉sigma 先设 0确认纯两相流动能跑。然后加回 tension.h再设 sigma。如果这时候又崩问题就锁定在曲率初始化。用 gdb 跑gdb ./dam_break看到崩溃调用栈在哪个函数再回查对应的头文件比盯着屏幕猜快得多。5.3 界面糊成一片阈值设太大或 adapt 没在循环里触发现象运行完导出画面水气界面不是一条干净曲线而是一片锯齿状台阶或者网格图上看不到任何局部加密。原因一是adapt的阈值太大误差估计认为不需要加密二是 adapt 只写在event init里只执行了第一次三是 minlevel 设置得太高比如 minlevel7把整个网格撑满了反而看不到自适应效果。解决把adapt放进event adapt (i)保证每个时间步都调用minlevel 先设 4maxlevel 设 LEVEL 2观察日志里网格总数是否随时间变化。如果总数一直不变说明 adapt 条件没有被触发把阈值调小一个量级再试。5.4 MPI 并行跑不出结果先单核后多核的排查顺序现象单核跑得好好的一上 mpirun 就报错或者结果和单核完全不一样。原因BASILISK 的并行依赖网格分区qcc 默认没有开启 MPI 宏。如果你直接用mpicc去代替 qcc宏和头文件展开顺序不对会出现各种诡异问题。解决先用普通 qcc 单核跑通确认算例本身没问题然后重新编译加上-D_MPI1qcc -O2 -D_MPI1 -o dam_break_mpi dam_break.c -lm mpiexec -n 4 ./dam_break_mpi运行时注意日志里的网格统计确认四个进程各自分到的格子数大致均衡。如果某个进程的格子数接近 0多半是初始网格太小把 LEVEL 调高一级再并行。5.5 时间步长越跑越小表面张力 CFL 的约束现象日志里的 dt 一开始是 1e-3跑几十步后突然掉到 1e-5甚至 1e-6运行速度肉眼可见地变慢。原因表面张力对显式时间步长有严格的稳定性约束网格在界面附近越细允许的 dt 越小。这是物理约束不是代码 bug。界面此时往往进入高曲率时期比如液滴颈缩断裂阶段。解决先检查 sigma 是否被设得过大。如果 sigma 确实需要就接受小 dt把它当成高分辨率拉普拉斯求解的必要成本。另一个办法是适当提高粘度让界面演化变慢换取较大的时间步也可以用更高阶的离散格式或换 smaller-step 的自适应策略。最后实在不行降低 maxlevel让界面附近不要那么细dt 会很快回升。6. 进阶用 GIF 动画和日志验证你的 BASILISK 结果6.1 用 view save 生成动画帧BASILISK 自带的可视化模块是 view.h它生成的不是网格文件而是一张张渲染图。把它加进你的算例可以按固定时间间隔保存 PNG 或 MP4整个过程和求解器共用同一套网格数据不用额外导出 VTK。常见写法是这样#include view.h event movie (t 0.02; t 1.) { clear(); view (fov 0.5, tx 0.5, ty 0.5); draw_vof (f, lw 1.5); squares (f, spread -1); save (dam.mp4); }draw_vof画出界面曲线squares用色块显示体积分数场spread -1会把数值范围做对称拉伸。save的扩展名决定输出格式mp4 需要系统里有 ffmpeg如果没装改成dam-%d.png之后再用外部工具拼成 gif。这段代码加进你的 dam_break.c 里重新编译运行结束后目录下就会多出动画文件。6.2 用 log 与 Python 做体积守恒验证视觉上看动画通过后下一步应该做定量验证。Basilisk 运行时会在当前目录生成 log 文件里面按时间步记录 t、dt、迭代次数和网格统计。体积守恒是多相流最基本也是最重要的检查项可以把体积分数全域积分拉出来看是否随时间变化。import numpy as np import matplotlib.pyplot as plt rows [] with open(log) as fp: for line in fp: line line.strip() if not line or line.startswith(#): continue rows.append([float(x) for x in line.split()]) rows np.array(rows) t rows[:, 0] dt rows[:, 1] plt.plot(t, dt) plt.xlabel(t) plt.ylabel(dt) plt.savefig(dt.png)不同版本的 log 列顺序可能不同第一次跑先在终端执行head -20 log看列名再对号入座。如果 dt 曲线出现突然的跳变尖峰说明表面张力或网格自适应在某个局部出现了数值不稳定回头检查那个时刻网格是否加密过头或阈值太松。6.3 拿解析解对比是最后的定心丸动画和守恒能判断“对不对”但要判断“准不准”必须回到解析解或文献数据。对溃坝算例可以记录液锋前沿位置随时间的变化对比经典半经验公式对液滴震荡算例可以测量液滴振荡频率对比无粘解析频率。跑三步能快速定位问题第一步用最粗网格跑一遍第二步把 maxlevel 提高一级第三步比较两次结果里你关心的量变化了多少。如果两次差异超过 10%说明当前网格还没有收敛结果进不了正式报告。我自己的习惯是每次换一台环境重新装 BASILISK换系统或换编译器后第一件事不是改代码而是编译官方 examples 里自带的一个最小算例确认 qcc、网格和可视化整条链路都通再往里填自己的物理模型。省下来的排错时间比想象中多得多。BASILISK 的学习曲线陡但它把“看清水和空气怎么打架”这件事的颗粒度交到了开发者手里这个自由度很值得希望帮到你。本文还有配套的精品资源点击获取