||
刚上手第一性原理计算的人,应该都遇到过类似“-bash: xxx: command not found” 或者 “xxx: error while loading shared libraries: libxxx.so: cannot open shared object file: No such file or directory”的错误。如果自己编译过abinit、QE、siesta等软件,一般也遇到过已经安装了某个库,configure时却无法识别的情形。这些问题都由环境变量设置不当导致。软件安装后,需设置PATH、 LD_LIBRARY_PATH、LIBRARY_PATH等环境变量,才能正常运行。随着软件越装越多,~/.bashrc也越来越复杂,充斥着这样的设置:
这种方式有几个缺点,最严重的就是要输很多环境变量名。有几个环境变量名很长,一旦输错一个字母,轻则软件不能正常运行,重则连ls、vi等命令都不能用了。我就曾经见过有人手滑,把LD_LIBRARY_PATH中的某个R打成了T。相比于直接操作环境变量,更为安全的方式是定义一个函数,通过这个函数修改环境变量:
这样每个环境变量名只需输入一次,极大地降低了出错的概率。但这种方法并非十分完美。每次添加新软件的配置信息,或者移除旧软件,都要修改~/.bashrc并重新登录,非常麻烦。如果一台服务器由几个人共用,管理员需要为每个人准备一份~/.bashrc。况且不同软件间一般存在依赖或冲突关系,例如QE依赖MKL和MPI,如果这两个组件未正确设置就会报错;ADF和ORCA等软件所需要的特定版本的MPI,一般又和系统中已安装的MPI冲突。通过修改~/.bashrc来设置软件运行环境,每次都要改动很多地方,既费时费力,又容易出错。如果有程序能根据需要自动设置环境变量,使用软件时就会轻松很多。
环境模块系统(Environment module)就是为解决这类问题而设计的软件。目前环境模块系统有Environment Modules (又名Tmod)和Lmod,以及一个比较古老的Cmod和用shell script写的dotkit等几种。配置Tmod需要学习TCL,Lmod需要学习Lua。两门语言都相对小众,有一定的学习成本。另外,一些软件如Intel编译器、Gaussian、ADF等都提供了初始化脚本来设置运行环境。但无论Tmod还是Lmod都不建议用户使用这种脚本,也没有运行脚本的功能,而是提供了工具将之转换成各自的配置文件,这就不是很方便。于是就用Python自己写了一个简单的环境模块系统Pmod。软件原理不复杂,核心部分是这样一个函数:
shell把命令行参数传给python脚本,由脚本确定需要修改哪些环境变量并输出相应指令,再由shell执行该指令。Tmod和Lmod也是基于这种设计思路。目前Pmod已具有模块加载与卸载、自动解决冲突和依赖关系、查看模块内部配置信息和加载状态、根据名称搜索模块等功能,项目放在GitHub上:https://github.com/yhli1016/Pmod。
Pmod只依赖Python标准库,因此只要系统中安装了Python即可运行,对Python版本也无限制,2.x或3.x均可。具体安装过程如下:
① 下载源码并解压到安装目录,若供多人使用可解压到/opt,仅单人使用可解压到$HOME;
② 假设解压后源码目录为/opt/pmod,修改init/bash.sh,将PM_ROOT设置为解压后的源码目录;
③ 若供多人使用,将init/bash.sh链接至/etc/profile.d,仅单人使用则在~/.bashrc中添加一行“source /opt/pmod/init/bash.sh”;
至此安装完成。重新登录系统后,输入module av即可得到如下输出:
这些都是例子中预设的模块,仅作示范用。用户需要根据需求,添加自己的模块。在讲解如何添加模块之前,先简单介绍Pmod的内部架构。
Pmod由一个初始化脚本init/bash.sh,一个命令行程序bin/modcmd.py和两个Python包组成。pmod包中定义了三个类:Module、SandBox和ModMananger。Module对应负责存储环境变量、命令别名等配置信息。ModManager负责解析依赖关系,以及加载和卸载模块。SandBox负责收集各模块的中存储的配置信息,并输出相应的shell命令。在modulefiles包的setup模块中从ModManager类创建了一个对象mod_manager,并通过create_mod()方法向其添加了各个模块。最后bin/modcmd.py导入mod_manager,通过load_mods()、unload_mods()等方法完成用户指定的操作。
添加模块主要通过ModManager类的create_mod()方法完成,调用方式如下:
第一个无名参数为模块名称,如果模块不存在则创建之,已存在则向该模块添加配置信息。除mod_class关键字外,剩下的关键字参数最终都传递到Module类的add settings()方法。该方法接受的关键字参数如下:
preset和destination两个参数指定向该模块添加哪些环境变量配置信息。以openmpi为例,preset=“mod”和destination=“/opt/openmpi/3.1.3”会把/opt/openmpi/3.1.3/bin添加至PATH,/opt/openmpi/3.1.3/lib添加至LIBRARY_PATH和LD_LIBRARY_PATH,/opt/openmpi/3.1.3/include添加至C_INCLUDE_PATH和CPLUS_INCLUDE_PATH。当preset值为mod时,会自动补全bin、lib、include等子目录,但取其它值时不会自动补全。例如,若要将/opt/gpaw/1.4.0/bin添加至PATH,就必需指定preset=“path”和destination=“/opt/gpaw/1.4.0/bin”。
environ参数指定无法通过preset和destination设置的环境变量配置信息。这个参数是一个列表,每个元素是一个由三个元素组成的元组。元组中第一个元素指定对环境变量进行何种操作,取值为reset、append和prepend三种,分别代表重设、在尾部追加字符串和在头部插入字符串;第二个元素为环境变量名称;第三个元素为修改环境变量所用的字符串。在上面所给出的GPAW例子中,environ参数表示将环境变量GPAW_SETUP_PATH重设为/opt/gpaw/1.4.0/data/gpaw-setups-0.9.20000。此外,environ参数多用于加载编译器时设置相关环境变量:
这些设置最终转化为如下shell指令:
depend和conflict参数指定与当前模块所依赖的模块,以及与之冲突的模块。每个参数都是一个列表,列表内元素为模块的名称,具体参见上面GPAW的例子。需注意,在加载和卸载模块时,依赖和冲突关系会被递归展开。例如,IntelMPI依赖IntelCC,又与各OpenMPI冲突;作为依赖IntelMPI的VASP,其depend参数只需包含IntelMPI,而conflict参数无需设置:
在加载VASP模块时,Pmod会自动检测并卸载各OpenMPI模块,并加载IntelCC和IntelMPI模块。当有大量模块依赖同一模块时,可以从Module类派生新的类,重载__init__()方法,在其中将所依赖模块加入depend列表,然后将派生类作为mod_class参数传给create_mod()方法,如下图所示:
这样便可进一步降低出错的概率。详细的例子参见examples/hierarchy。
command参数指定额外的初始化命令,是一个由字符串组成的列表。每个字符串即一条命令,一般用来运行初始化脚本。alias参数指定创建哪些命令别名,是一个由元组组成的列表。每个元组包含两个字符串:别名和命令。这两个参数的例子如下:
罗列所有可用模块: module avail/av
查看各模块状态:module status/stat
如果加载了某模块后不慎修改了相关环境变量,该模块就会被标记为broken。例如,IntelCC模块将CC设置为icc,若将CC修改为gcc,则该模块“损坏”。
查看已加载模块:module list/ls
该命令是module stat的简化版,在Pmod内部也由同一个函数完成。
查看模块内部配置信息:module info/show/display xxx
检查模块是否正常加载:module diagnose/probe xxx
搜索模块(支持正则表达式):module search regex
加载模块:module load/add xxxx
自动模式下自动加载依赖模块并卸载冲突模块
卸载模块:module unload/remove/rm/delete/del xxxx
自动模式下卸载指定模块时,会同时卸载其依赖模块。但若依赖模块仍被其它模块使用,则不会被卸载
卸载所有已加载模块:module clean/purge
重新加载已加载模块: module reload/update
各软件间往往存在依赖或冲突关系,因此仅仅加载某个模块,软件一般无法运行。Pmod提供了自动模式解决这个问题。在自动模式下,除了加载指定模块外,还执行如下操作:
① 加载依赖模块
② 卸载冲突模块
③ 卸载因第①②步操作而无法运行的模块
④ 重新加载因第①②步操作而需重新加载的模块
在卸载模块时,还执行如下操作:
① 分析当前模块的依赖关系,并卸载其中不再被其它已加载模块依赖的模块;
② 卸载因第①步操作而无法运行的模块
③ 重新加载因第①②步操作而需重新加载的模块
自动模式保证了已加载的模块都可用,不足之处就是module load和module unload命令的效果和用户预期有差别。例如,VASP依赖IntelMPI,在不卸载VASP的情况下无法卸载IntelMPI;而卸载VASP的同时,也会连IntelMPI一起卸载。因此,Pmod提供了-f/--force_no_auto选项以禁用自动模式。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-11-23 19:25
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社