很多刚接触Fleur的人,第一反应是找现成的预编译二进制,但真到了自己的工作站或者集群上,最常遇到的事情恰恰是:怎么用gcc和OpenMPI把Fleur从源码完整编译出来。这个gcc-openmpi版编译并不是简单敲两行make命令就收工,背后牵扯编译器匹配、MPI编译器包装器、可选依赖库联动、优化选项选择等一连串环节。我前后在好几台环境不一样的机器上编译过Fleur,踩过不少坑,这次把流程和问题一次性说清楚,希望你能少走几步弯路。
Fleur是做什么的,为什么需要自己编译,以及为什么我坚持用gcc和OpenMPI这套组合,我会先讲清楚。接着进入编译前的依赖准备,再分别讲CMake方式和传统Make方式的操作细节,随后是错误排查和性能验证。整篇文章完全以实操为主,不会绕到深奥的理论里。如果你正打算在自己的Linux环境里搞定Fleur,这篇可以直接当操作手册用。
1. Fleur是什么,为什么要编译gcc-openmpi版
1.1 一个真正需要“动手编译”的科研级工具
Fleur是用于电子结构计算的DFT代码,核心方法基于FLAPW(全势线性增广平面波)。简单说,它用一套非常精确的基函数来描述晶体中的电子状态,适合处理磁性材料、表面、界面等需要高精度的体系。和很多一键安装的软件不同,Fleur是典型的科研级Fortran/MPI程序,发布形式就是源码,拿到源码之后,你需要针对自己的MPI环境、编译器、数学库再完成一次构建。编译成功之后,你得到的是一系列可执行文件,常用的包括fleur、fleur_ MPI相关工具以及输入文件预处理程序。
我遇到的大部分人想编译Fleur,是因为有一个并行计算任务要跑。Fleur支持MPI并行,而最方便获得、也最容易被gcc兼容的MPI实现,就是OpenMPI。因此“gcc-openmpi版”其实是大多数Linux工作站上最简单可行的组合:编译器用GNU gcc/gfortran,并行环境用OpenMPI,两者都是开源、免费、系统包管理器里通常自带。也正是这个原因,这个组合成了我在通用服务器上的默认选择。
1.2 为什么优先选gcc和OpenMPI,而不是其他组合
很多人问我,要不要换商用编译器?我的看法是,如果你的机器上没有特殊要求,先别折腾。gcc和gfortran是GNU工具链的一部分,对Fortran标准支持非常完整,Fleur这种学术软件通常也会优先在GNU工具链上测试。你不需要授权文件,不需要设置复杂的License环境变量,装好就能用。OpenMPI同样是开源MPI实现,生态成熟,和gcc配合时的兼容性也最好。使用mpif90这个包装器编译Fortran时,它会自动把MPI头文件和库路径加进去,这比手动往LD_LIBRARY_PATH里补路径省心得多。
另一个原因是排错成本低。用系统自带包管理器安装gcc和OpenMPI,版本通常经过了一定验证,互相之间不会出现莫名其妙的ABI不匹配。商用编译器本身没有问题,但和OpenMPI版本之间的兼容性需要额外测试,一旦出现符号找不到或者库版本冲突,排查起来非常费时间。对于编译Fleur这种依赖链长的项目,稳定比“理论上的极致性能”更重要。等你在gcc-openmpi版本上跑通了流程,后续可以再慢慢尝试其他编译器组合,那时你有了一份可以对比的基线结果。
2. 编译之前的准备工作
2.1 环境规划:先确认工具链版本匹配
编译Fleur之前,不要直接跳到make,先把环境摸清楚。你需要知道三件事:gcc/gfortran版本、OpenMPI版本、以及系统可用的BLAS/LAPACK库。我通常用一个简单命令一次看清:
gcc --version gfortran --version mpirun --version这三条输出会告诉你基础工具链是什么状态。需要注意,gcc和gfortran应该来自同一个版本,否则Fortran编译器和C编译器版本割裂,后面链接阶段容易出现类型长度不一致的问题。OpenMPI的版本不用追求最新,稳定就好。如果你是在集群上,通常会有module环境管理软件,使用前先看有哪些MPI模块:
module avail选一个和系统gcc匹配的openmpi模块加载。加载之后,再跑一次which mpif90和mpirun --version,确认实际生效的确实是你要用的那一个。我见过太多次“以为自己用3.1版本,结果module load后为什么还是旧版”的情况,最后发现是.bashrc里写死了旧路径。这个问题下面排查时会专门讲。
2.2 依赖库清单
Fleur编译时会用到几个关键依赖,最基础的是Fortran编译器、MPI库、以及BLAS/LAPACK。BLAS/LAPACK是线性代数库,Fleur的矩阵对角化和线性代数运算都依赖它们。系统里一般有OpenBLAS或者参考版LAPACK,建议优先用系统包管理器安装好的版本,因为编译器配置好之后,CMake能自动找到它们。
此外,新版本Fleur会用到NetCDF和HDF5,用于IO和并行文件输出。这两个不是必须的,但是如果算例需要输出大数据量,就很建议开启。我一般在Debian/Ubuntu系系统上用apt完成基础依赖安装:
sudo apt install gcc gfortran libopenmpi-dev openmpi-bin libblas-dev liblapack-dev libnetcdf-dev libhdf5-dev在CentOS/RHEL系系统上,则要区分老版本和新版本。以常见的Rocky Linux 9为例,包名差不多,比如gcc-gfortran、openmpi-devel、blas-devel、lapack-devel、netcdf-devel、hdf5-devel。装完后注意openmpi的库路径,因为有些发行版把OpenMPI安装在/usr/lib64/openmpi下面,需要手动把mpif90加入PATH,否则命令行根本找不到。
2.3 获取源码与目录结构
确定依赖齐了,再去拿Fleur源码。Fleur的官方源码仓库是公开的,你可以从它的项目主页或者代码托管平台直接克隆或下载压缩包。推荐用git clone,这样后续切换到特定版本或者追踪更新会比较方便:
git clone https://gitlab.com/fleur-project/fleur.git cd fleur目录结构方面,通常会有src、tests、documentation这些目录。官方文档会写明不同版本适合的构建方式。我接触过的版本里,新版本推荐CMake,老一点版本仍有成熟的Makefile体系。建议先去documentation目录看一眼,确认当前版本到底推荐哪种构建方式,不要凭经验套用。这一步虽然不起眼,但能省掉后面许多“配置半天却编不过”的麻烦。
3. 核心实操:CMake方式编译
3.1 准备独立的构建目录
CMake方式最推荐的做法是创建单独的build目录,不要直接在源码目录里编译。这样做的原因很直接:CMake会生成大量中间文件,如果直接放在源码树里,后续想清理重新编译会很痛苦,而且不同配置之间还容易互相污染。我习惯在Fleur源码根目录下执行:
mkdir build cd build然后开始配置。如果这一步你发现CMake版本过低,先升级CMake,建议用3.16以上版本,太老版本对很多现代项目支持不好。
3.2 配置CMake参数
CMake配置是编译Fleur最关键的一步。不要用默认配置直接跑,你需要显式告诉CMake要使用哪些MPI库、是否启用NetCDF/HDF5、是否开启OpenMP。一个我常用的配置命令是:
cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_Fortran_COMPILER=mpif90 \ -DCMAKE_C_COMPILER=mpicc \ -DWITH_MPI=ON \ -DWITH_OPENMP=ON \ -DWITH_NETCDF=ON \ -DWITH_HDF5=ON这里的-DCMAKE_Fortran_COMPILER=mpif90尤其关键。mpif90是OpenMPI提供的Fortran编译器包装器,你直接指定它,CMake会在编译和链接阶段自动加入MPI相关的头文件搜索路径和库搜索路径。如果不指定这个,而是直接把gfortran当作编译器塞进去,那么即使CMake自己找到了MPI,也容易出现MPI头文件找不到或者运行时库不匹配的问题。
另一个值得注意的参数是CMAKE_BUILD_TYPE=Release,这个选项会开启编译优化。如果你不设置,默认可能是空优化,跑出来的可执行文件性能会很吃亏。有些人在编译科研软件时为了求稳,经常不加优化直接make,结果算个几百原子体系慢得离谱。Release模式一般对应-O2或者-O3,对Fleur这种计算密集程序来说是正确选择。
如果你的环境里NetCDF和HDF5不是系统默认路径,那CMake大概率找不到它们,此时不要慌,手动指定安装路径:
cmake .. -DNETCDF_INCLUDE_DIRS=/path/to/netcdf/include \ -DNETCDF_LIBRARIES=/path/to/netcdf/lib/libnetcdf.so \ -DHDF5_INCLUDE_DIRS=/path/to/hdf5/include \ -DHDF5_LIBRARIES="/path/to/hdf5/lib/libhdf5.so;/path/to/hdf5/lib/libhdf5_hl.so"这个路径配置比较繁琐,但一次性写清楚,后面会省去大量链接错误。
3.3 编译与安装
配置完成之后,CMake会生成Makefile,然后就可以编译了。直接使用:
make -j4-j4表示用4个核心并行编译。如果你的机器核心多,可以适当调大,比如-j8或-j16。但不要盲目调得非常高,编译时每个任务都会占内存,Fleur的源码编译内存消耗不算小,如果开的并行任务太多,机器会直接卡死。我一般建议按物理核心数的一半到四分之三来调,比如8核机器用-j6就差不多了。
编译成功之后,你会在build目录下找到生成的fleur可执行文件。官方还支持make install把可执行文件安装到指定目录,这样后续使用就不用每次进build目录。安装时指定前缀:
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/opt/fleur make install这个步骤看个人习惯,我更喜欢安装到统一的地方,因为后续写作业脚本时路径固定,不容易出错。
3.4 验证安装
编译和安装都完成,不代表能直接用,至少要做一次简单验证。最好的方式是从Fleur自带的测试算例里选一个最小的跑起来,确认MPI可以正常启动,计算能完成并与参考结果对比。
我通常先跑一个不带MPI的基础算例测试:
cd tests ./run_tests.sh如果测试脚本能全部通过,说明你这套gcc-openmpi编译产物基本没问题。如果测试脚本依赖的外部工具太多,也可以手动找一个最小输入目录,比如一个单原子或者简单晶体结构的体系,利用fleur的输入文件预处理程序生成输入,再提交任务。这里不展开,后面会专门讲验证时怎么判断结果是否正常。
4. 传统Make方式的编译路线
4.1 什么时候需要回到Make方式
不是所有版本的Fleur都默认支持CMake,我遇到过一些老版本项目结构里只有Makefile。这种情况下,如果强行套CMake,反而会浪费时间。判断方法很简单:源码根目录下看有没有CMakeLists.txt,如果没有,那就老老实实用传统Make方式;如果有,但你看到官方文档依然在推荐Makefile路线,也可以直接用传统方式。两种方式最终产物是一样的,不存在谁更“高级”的问题。
4.2 修改Makefile中的关键变量
传统Make方式下,Fleur编译的核心是修改make.settings或者Makefile里的配置项。你需要重点关注几个地方:
- Fortran编译器设置为
mpif90 - C编译器设置为
mpicc - 数学库路径指向系统BLAS/LAPACK
- NetCDF/HDF5开关按需打开
具体的变量名在不同版本里略有差异,我通常的做法是把Makefile打开,搜索FC、F90、CC这些经典变量名。一个简单示例:
F90 = mpif90 CC = mpicc COMMENT = gcc-openmpi如果官方提供了示例配置文件,比如随源码分发的make.settings里包含几种编译环境模板,那你直接选择贴近“GNU + OpenMPI”的模板,然后按实际路径微调即可。不要从零开始写,那样很容易漏掉某个必需的宏定义。
4.3 编译过程与结果对比
设置好之后,同样执行:
make -j4传统Make方式编译时,屏幕上会滚动大量编译信息。这里有两点心得:第一,如果出现Error 1,不要只看最后几行,要往前面翻真正的报错语句,因为错误可能只是某个对象文件编译失败,后面的链接无法进行;第二,不同节点如果有多人同时在编译,磁盘IO可能很高,编译速度会下降,这是正常现场,不用过度焦虑。
编译完成后,生成的可执行文件一般就在bin目录或者源码主目录。和CMake方式相比,传统Make方式需要手动确认的细节更多,但好处是你能更清楚地看到Fleur的依赖关系。我给一个建议:如果你只是要快速用起来,优先用CMake方式;如果项目是老版本,或者你需要深度修改源码内部编译选项,传统Make方式反而更加直接。
5. 常见编译错误与排查实录
5.1 找不到mpif90
这是新手最容易遇到的第一道坎。明明安装了openmpi,但在终端输入mpif90还是提示命令不存在。原因几乎都是环境变量PATH里没有包含openmpi的bin目录。在有些发行版上,openmpi被装到/usr/lib64/openmpi/bin,而不是默认的/usr/bin。你可以先用find确认位置:
find / -name mpif90 -type f 2>/dev/null找到后,把bin目录加入PATH,或者使用module load加载对应环境。如果确认系统里没有装openmpi的开发包,那就先安装libopenmpi-dev或openmpi-devel,开发包里才有mpif90。仅仅是openmpi-bin或者openmpi运行包,往往不包含编译包装器。
5.2 gfortran和gcc版本错位
在我一次实际编译中,gcc是12版本,但gfortran却是9版本,结果链接时出现Floating point exception和奇怪的符号解析失败。这个问题常见于多年没更新的机器上,用户可能为了某个软件手动装了新版gcc,却没有同步升级gfortran。解决方法是让两者版本对齐。在Ubuntu系上可以通过update-alternatives切换默认版本,也可以用模块系统加载同一版本的工具链。
验证方法很直接:
gcc --version gfortran --version对比输出中的版本号。如果版本不一致,后面编译Fleur时出现任何晦涩的报错,第一怀疑对象就是这个。我后来只要换机器,第一件事永远是同时检查这两个版本号,避免在坑里反复爬。
5.3 链接NetCDF/HDF5报错
启用NetCDF和HDF5之后,链接阶段经常出现cannot find -lnetcdf或者cannot find -lhdf5。这并不代表库没装,很可能是CMake没有找到正确的库路径。我在3.2节里已经写了手动指定路径的做法,这里再强调一个思路:先确认库文件存在,再确认它的架构和编译器匹配。
ls /usr/lib/x86_64-linux-gnu/libnetcdf.so如果你从源码编译过NetCDF,它可能安装在/usr/local/lib,而CMake默认不会搜索这个路径。不用担心,指定NETCDF_LIBRARIES和NETCDF_INCLUDE_DIRS即可。另一个小技巧是打开CMake的调试日志,让它显示具体搜索了哪些路径:
cmake .. --debug-find这能帮你看清CMake在找什么、为什么找不到。
5.4 优化导致的数值异常
Fleur是数值计算程序,优化等级过高有时会引发“看似编译成功、一跑就错”的诡异问题。我在一台新机器上用-O3编译Fleur,所有测试用例能编译通过,但计算某个磁性体系时能量出现非物理的振荡。后来我改成-O2,问题就消失了。这不是gcc本身的错,而是个别计算模式在激进优化下触发了编译器对浮点运算的重排。
如果你也遇到类似问题,建议分两步排查:先按默认优化模式编译并跑一个小算例,确认结果正常;再尝试提高优化等级并对比结果。Fleur这种计算代码,稳定性永远是第一位。我个人的经验是,除非你非常清楚自己的体系对浮点误差不敏感,否则不要为了性能强行开-O3。真追求极限性能,更多应该从MPI并行效率、编译选项中的-march=native是否适用等方向入手,而不是简单堆优化等级。
5.5 编译中途内存耗尽
make -j16在16核机器上跑得好好的,在只有8G内存的机器上就经常被系统杀掉进程,日志最后出现Killed。这种问题多见于源码编译的并行任务太多。解决办法是减少并行度,比如make -j4。如果你确实需要快速编译,可以临时增加swap空间,但我更推荐控制编译并行任务数。计算节点上跑着重任务时,也不要同时开大量编译任务,否则会拖垮整个节点。
5.6 编译错误速查表
| 错误现象 | 常见原因 | 解决办法 |
|---|---|---|
mpif90: command not found | OpenMPI开发包未安装或未加入PATH | 安装openmpi-devel,或加载module |
Cannot find -lnetcdf | NetCDF库路径未配置 | 手动指定NETCDF_LIBRARIES |
undefined reference to ... | 编译器版本不一致或库顺序错误 | 对齐gcc/gfortran版本,检查链接项顺序 |
Killed | 并行编译导致内存不足 | 降低-j参数,增加swap |
| 编译通过但计算异常 | 优化等级过高或硬件问题 | 改-O2,先跑小算例验证数值 |
这张表并不是穷尽所有情况,但覆盖了我最常遇到的几类。真遇到表外错误时,我的排查习惯是:从屏幕上的第一条Error开始看,而不是从最后一行看。很多链接错误是前一两个编译错误导致的连锁反应,解决根源后,后面一串问题都会消失。
6. 编译结果的功能与性能验证
6.1 算例验证判断编译环境是否正确
编译成功是第一步,能不能算出正确结果是第二步。我强烈建议编译完先跑一个极其简单的体系,比如或者一个已知结果的金属晶胞。Fleur自带测试集里通常有基础算例,可以运行测试脚本一键验证。如果没有,就用输入文件预处理工具生成一个最小输入,然后运行:
mpirun -np 2 ./fleur -inp inp.xml这里-np 2表示用2个MPI进程。观察输出文件里是否出现正常迭代收敛的过程。最终得到的能量数值,应该和官方参考值在可接受误差范围内一致。这一步非常重要,因为如果编译器版本或库版本有细微问题,编译阶段不会报错,但计算结果可能差了千分之几,这对DFT计算来说是完全不可接受的。
6.2 并行效率的初步检查
Fleur使用MPI并行,你还需要确认并行扩展没有问题。做法很简单:用一个中等规模的算例,分别用1、2、4、8个MPI进程跑相同步数,记录每次运行时间。如果看到4进程比2进程明显快、8进程比4进程快,说明编译出的OpenMPI版本工作正常。如果出现了“多进程反而更慢”,常见原因是节点之间的网络通信开销很大,或者你的输入文件体系太小,并行粒度不够。
在多节点环境上跑之前,还要检查OpenMPI是否能正确跨节点通信。通常用mpirun --hostfile指定节点列表,如果无法启动,大概率是SSH免密或者同一用户权限的问题。这一步虽然不属于编译本身,但我把它归到“编译后验收”里,因为只有确认MPI环境正常,这份编译工作才算真正完成。
6.3 与参考结果的一致性注意点
我遇到过一种情况:编译成功、测试脚本也过了,但自己算的某个体系自洽场能量和论文里不同。这种差异不一定来自编译错误,更常见的是输入文件的参数设置不同,比如平面波截断、K点密度、磁性初始设置。因此,“验证编译结果”不等于“要求绝对复现某篇论文”。你真正需要对比的是Fleur官方针对标准算例给出的参考输出,或者你自己在完全相同输入条件下用另一种编译器组合得到的结果。只有控制变量一致,差异才能指向编译问题。
7. 我的几点实操体会
编译Fleur这件事,最早我也觉得是个“一次性的苦力活”,编完就完事了。后来在不同机器上反复操作,我慢慢意识到,真正重要的事是在编译之前把环境理解清楚。GCC和OpenMPI的组合之所以适合大多数人,是因为它把“能用”的门槛降到了最低。你不需要为授权烦恼,不需要为环境变量额外写脚本,出了问题随口问一句同行,大概率对方也用的是这套工具链,能很快给你参考。
我在实际操作中还形成一个小习惯:每次编译都在build目录旁边保留一份完整的cmake配置命令,写成shell脚本保存下来,比如config.sh。这样换机器、换版本时,我可以直接修改几个路径参数,重新运行脚本就能用。不同机器上的BLAS/LAPACK路径千差万别,靠记忆迟早出错,脚本才是可靠的。
另一个值得记住的点:不要一上来就尝试“最复杂”的配置。如果第一次编译,先关掉NetCDF,只保留MPI和OpenMP,等基本版本能编译、能跑通测试,再逐步打开其他选项。这样一旦出错,你至少知道是新选项引入的问题,不需要在整条依赖链里大海捞针。我自己就在NetCDF和HDF5同时打开的情况下遇到过报错,最后发现是HDF5高版本改变了库的依赖顺序,单独把HDF5先加上就很容易定位。
如果你还有余力,编译完之后可以再做一个简单的性能对比:同一算例分别用CMake方式和传统Make方式编译出的可执行文件跑一遍。虽然两种方式最终生成的功能相似,但不同构建配置可能会影响默认的优化参数和链接选项,这个对比能让你对自己手头这套gcc-openmpi环境更心里有数。
最后,提醒一句,编译日志不要删。出现奇怪问题时,历史日志能帮你判断到底是哪次改动引入了问题。我习惯把每次的CMake配置参数和编译日志按日期归档,有时候排查几周前的问题,靠的就是这些记录。这套方法不止适用于Fleur,几乎所有源码编译的科研软件都适用。希望这篇gcc-openmpi版编译经验对你有用。