前言
在海洋科学专业混了四年,终究还是要转行进入IT行业。想想当初从小立志要成为科学家的自己最后还是成了码农,也真是醉了。毕竟还是用了三年时间来认清一个事实,那就是这样的科研圈子确实并不适合自己。不过虽然已经转行,但是由于先前的海洋学项目还没有结题,所以还是得折腾一些数值模式,也算是为ASC15备战了。
下面是我这两天配置FVCOM模式的一些成果,主要实现了在全新安装的CentOS 6.6系统上面,使用最新的Intel Parallel Studio 2015编译环境,基于最新的FVCOM 3.2源代码,成功将该模式编译并运行。下面是具体的操作步骤、注意事项以及相关资源下载地址。此外,本文若有更新,将仅在我的博客上发布。
综述
所谓数值模式,本质上就是一种主要用于科学模拟计算的复杂软件。相比于一般的应用软件,由于其运行环境更为复杂多变,可能是串行执行,也有可能需要配置在种种不同架构的并行集群系统上面,因此这类软件通常会以源代码的形式发布,用户使用时需要根据自身运行平台的情况以及需求,自行调整配置并编译。这类软件由于除了在执行并行节点间的通讯,以及文件IO之外,其余情况下都只是在做纯粹的运算工作,因此与操作系统本身的耦合程度其实不高,换句话说也就是不会依赖于复杂的系统函数库等等。
对于FVCOM模式,在编译过程中主要依赖一些科学计算方面的库,如利用netCDF库来输出运算结果等。对于这样的函数库,通常情况下无法把Linux发行版Repo里面的预编译二进制安装包直接拿来使用,因为数值模式本身需要被支持MPI并行环境的编译器所编译,所以基本上肯定会与预编译的软件包(一般使用GCC编译,优化级别O2)存在二进制不兼容,其结果就会体现为模式代码可以通过编译,但是无法正确链接生成可执行文件。
因此,为了避免用户在使用模式之前还要去费力地从官网下载源码,自行配置编译环境来编译这些依赖库,FVCOM自带了它所用到的所有依赖库的源代码,并进行了一定程度的修改和整合,使得用户可以一键自动编译。但可能是由于源码维护人员更新依赖库版本的积极性不高,而这些库的代码质量也并不是很好,老版本的兼容性较差,从而导致用户在较新的编译环境中编译这些老旧的依赖库时,常常由于各种原因而失败。再加上数值模式本身的代码实现上很多地方也并不严谨,这些原因共同导致了研究者们非常不愿意更新他们所使用的集群配置、编译环境以及数值模式版本,因为这些折腾起来确实烦人,而且也很难做好。本文所做的工作就是完全使用最新的软件环境,编译了一遍最新版本的FVCOM模式,把各种可能的坑都踩了一遍,希望这两天的折腾所产出的这篇文章能够为以后遇到同样需求的人节省一些时间。
操作系统安装
首先从官网下载CentOS 6.6的安装镜像,只使用DVD1就可以,DVD2里面是一堆附加的软件包。至于安装的具体步骤,网上教程铺天盖地,此处不予赘述。安装的时候注意把GCC编译工具链装上,选择Minimal Desktop配置进行安装就可以了。如果用U盘安装系统的话,千万注意要把grub装到/dev/sda,不是sdb,也不是sda1,不然无法引导!装好之后,愿意的话可以yum update -y升级一下所有软件包。
编译器安装与配置
首先从Intel官网下载最新的Linux平台编译环境,这个产品全称是Intel Parallel Studio XE 2015 Cluster Edition,可以免费申请30天试用版,或者为了省事也可以直接用后面提供的永久激活license。解压之后运行install_GUI.sh可以启动图形安装界面,需要激活的时候选alternative,然后选择那个license的位置,安装组件全选,之后一路下一步就好了。
安装完成之后,你会发现无法使用编译器的编译命令如icc等。Intel的产品都有这个毛病,就是非要你手动运行它那个设置环境变量的脚本。不过这也不难理解,集群系统毕竟是个多用户的使用环境,不可能由安装程序自动给每个用户来设置环境变量,所以还是手动好了。执行vim ~/.bashrc打开bash配置文件,不懂vim怎么用的自行百度,对于2015版本的编译器,在最下面添加这样一句:source /opt/intel/parallel_studio_xe_2015/psxevars.sh,就可以在下次登录shell的时候自动导入所有需要的环境变量了。如果你现在就要用编译命令,就直接先执行这句来设置好环境变量。
编译依赖库
首先吐槽一句今年最新版本的FVCOM里面还包含着不知道多少年前的netCDF库是几个意思?搞科研的毕竟不像搞IT的有Code Review以及公司规定的Programming Style,代码很多时候写不严谨的啊,各种跨平台不兼容啊尼玛……回到正题,首先进入FVCOM_source/libs目录,执行make直接编译。这里的makefile会自动将各个依赖库的源代码解压缩并进去编译。当然,如果你用本文中这么新的编译环境,编译过程肯定不会成功,会有不断的各种报错,几乎所有错误的解决方案都列在下面了。当然,如果想要省事,也可以直接用后面提供的修改过后的源代码,至少在本文给定的平台上(CentOS 6.6 x64 + Intel Parallel Studio 2015),可以确保一定能够编译成功。
编译FVCOM
首先把FVCOM_source目录下的make.inc_example文件复制为make.inc。然后按照文件里面的说明设置一下TOPDIR变量,也就是当前源代码目录的绝对路径。接着调整你需要的各种参数开关,最后直接执行make来编译即可。当然,在比较新的编译环境下,你肯定还是会遇到各种报错,本文下面就总结了一些常见的问题。
报错与解决方案
ncvalues.cpp: error: 'strncpy' was not declared in this scope
找到这个报错的cpp文件(属于netcdf库),将#include <string>修改为#include <cstring>,写代码的人在这里犯了个小错误,没有区分好这两个头文件的区别,前者属于C++而后者属于C语言。
./proto.h: error: conflicting types for ‘__log2’
编译metis的时候会出现这样的问题,这个错误同样是因为代码实现不严谨,修改它稍微复杂一些,我们需要应用一个patch来进行修正。下载好patch文件,修改目录名称之后,进入目录,执行patch -p1 < metis-4.0.patch即可。注意改完之后别忘了再把目录名称和结构改回来,不然还是会编译报错。
make: *** [func_pointer.o] Error 1
懂一点makefile以及编译器命令行语法的话就能够看出,这里的问题是由于使用命令行参数指定了空的include path,导致编译器没有读取到源文件名称所导致的。如果你没有在环境变量中预先设置好LIBPATH、INCLUDEPATH之类的变量,那么就去把make.inc文件中LIBDIR和INCDIR变量定义好,方法是解除对应行的注释。
mod_time.f90:405: Error: Can't open included file 'fjulian.inc'
诸如此类找不到文件的错误都是由于没有编译libs目录下的依赖库或者是没有正确编译好,先去编译它们就可以了。
Fatal Error: File 'netcdf.mod' opened at (1) is not a GFORTRAN module file
这个错误是由于链接到的netcdf库没有使用与编译fvcom相同的编译环境进行编译的缘故,从而导致了二进制文件不匹配。简而言之意思就是说,如果你用某个编译器来编译fvcom,那么一定要用同样的环境来编译netcdf,否则就会出错,而且即便不在编译期间报错,链接的时候也会出问题。
Error: 'b' argument of 'sign' intrinsic at (1) must be the same type and kind as 'a'
这个错误是因为在编译配置中启用了对双精度的支持,但是对于sign这个库函数,它要求两个参数必须为同样的数据类型。对于Fortran语言,常规写法的字面浮点常量如1.0默认为单精度,代码实现中的一点疏忽,再遇上了类型检查严格的编译器,就导致了报错。因为在编译为双精度时,这里的常量类型与变量类型就不匹配了。在编译配置中关闭对双精度的支持就可以解决问题,但是如果确实需要用到双精度,则应做如下修改,一共要修改两个文件四处语句。
IBIT1 = (INT(SIGN(1.,DX))+1) / 2改为:
IBIT1 = (INT(SIGN(1._SP,DX))+1) / 2
netcdf_file.f90: undefined reference to ……
又是netcdf库的问题,这个错误出现在当你以为已经要编译好fvcom的时候,会发现屏幕上刷出大片的“无法解析的链接”这样的报错。原因还是跟Intel的编译器有关,需要手动修改libs目录下的makefile文件,为CFLAGS和CXXFLAGS增加-DpgiFortran的宏定义即可。然后回到上一层目录重新编译netcdf库。至于为什么要这样做,可以参考这里。很多这样的能够把你坑得痛不欲生的报错,其解决方案都隐藏在这样的犄角旮旯里面……
fvcom: error while loading shared libraries: libproj.so.0
最后,终于把fvcom的二进制可执行文件编译出来了,但是你以为这样就大功告成了吗?Too young! Naive! 这个报错告诉我们找不到一个动态链接库文件,原因很简单,这个文件不在LD_LIBRARY_PATH里,手动给添加进去即可。进入执行make的那个FVCOM_source目录,运行一句:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libs/install/lib
就可以真正大功告成啦!为了省事的话,也可以直接将这句加入你的.bashrc文件里面,只不过需要做相应修改。此时尝试执行./fvcom,你会得到程序的错误提示,但是此时仅仅是在提示你没有指定输入文件而已。
测试样例
终于到了激动人心的跑测试样例的环节了!我们以Examples文件夹下的Estuary测试样例为例。首先按照这个目录中所给的make.inc_example文件中的编译配置,重新编译一遍FVCOM模式,确保关掉了不必要的选项,不然接下来读取配置文件的时候就会出错。然后进入run目录,执行ln -s ../../../FVCOM_source/fvcom ./把编译出来的可执行文件软链接到配置文件tst_run.nml的目录中去。最后执行./fvcom --casename=tst就可以运行模式了,不过可惜,这里报错了!报错的原因一般来说应该是Fortran语言对namelist的语法检查更为严格了,你需要把配置文件里报错的section里面,所有看起来像是字符串的东西,都用单引号括起来就可以了。修改后重新试图运行——成功!如果你没有将输出重定向的话,会在屏幕上刷出大量的信息,最好是重定向掉这些信息,让模式运行在后台就好了,或者是使用MPI并行运算。你会发现随着模式的运行,目录下不断生成nc文件,这些就是产生的数据了。拿去画个图,检验一下运行结果吧!
资源下载
- Intel Parallel Studio XE 2015 Cluster Edition安装包以及激活文件(提取密码:g4us)。个人还是建议直接从官网下载原版安装包,然后使用这里提供的license来激活。
- 最新的原版
FVCOM 3.2源代码以及修改过后便于在本文指定平台上编译的源代码下载。
结语
最后还是不得不吐槽一下,科研工作者的代码质量确实有点惨淡。以及,很多FVCOM的使用者如学长学姐们,经常会对源代码作出各种符合自己使用情况的更改,却不保存原版代码,反而将这些受过污染的源代码直接拷给不明就里的学弟学妹们。很多原本可以轻易弄清楚并解决的小问题,就是这样逐渐变得能够把不懂计算机的可怜孩子们折磨疯的。在科研编程领域,想要普及现代软件开发的基本概念如版本控制、代码审核等等,确实任重而道远。竟有老师满怀自豪地说“我编程从不写注释”,思想之落后由此可见一斑。此外,感谢小伙伴们的督促,使得我能够尽快把该折腾的这些东西都给折腾好。