“任何一个系统的存在及描述都离不开组成(material cause)、结构(formal cause)、相互作用(efficient cause)和功能(final cause)这四个因素。” –亚里士多德(Aristotle)的系统哲学:“四因说”
eMD用户手册–Tcl/VMD扩展
eMD开发Tcl扩展模块在于提供一套命令行编程工具,和其Python扩展模块提供一套编程语言库,两者设计目标是一致的:形成一套适合分子动力学模拟的编程框架库。成熟MD软件LAMMPS设计了一套特有的input脚本编程逻辑,形式上类似命令行,以至于有人真把它都成shell来使用(见其源代码tools/lammps-shell);但实际上这种命令行并不完善,if条件和loop循环流程并不是很成熟,使用上受限很大。而NAMD的配置引入了Tcl脚本编程,虽然有了Tcl的编程功能,但是NAMD的建模与模拟为较为封闭式模块,并不是面向可编程开放式设计,整个模拟过程用户很难干预,用户缺乏可编程的MD模块库,而这才是MD模拟的灵魂。
eMD设计则尽量克服现有软件的缺点,不仅提供一套C++实现的MD内核和建模辅助库,而且提供丰富的上层用户接口,通过SWIG工具实现Tcl和Python等多种语言的扩展;而这种扩展维系了eMD C++核心库的接口,不管是Tcl命令行,还是Python编程都是一样的“API接口”,可以灵活编程满足各种场合应用的需要。另外eMD选择了JSON配置格式,它们“自然地”对应了Tcl的{}字符串和Python的dict字典类型,对用户认识具有统一性。eMD提供Tcl扩展还有一个重要的原因是实现与VMD通过Tcl进行交互,VMD作为建模的前后处理工具,缺乏MD计算功能,虽然它可以与NAMD进行配合使用,但这种配合方式是非常松耦合的形式,数据传递等过程并不是非常有效,主要原因是NAMD开放的接口太少。
eMD的Tcl命令行编程框架的几大优势:
1. 采用SWIG包装了底层C/C++代码框架,Tcl层接近C/C++层的计算速度
2. 与底层C/C++同一套API,提供建模前后期和动力学计算接口,提供::emd命名工具和终端命令行补齐
3. 实现了std::vector
eMD可以编译为Tcl的package方式,这样可以在VMD的Tcl解释器中很容易引入,通过Tcl使用上eMD提供的功能。为了使用上的简便,eMD的Tcl扩展被编译为VMD的一个插件,可与VMD进行软件捆绑发布。已在MacOS X上提供VMD和eMD的封装App,该App已将eMD编译好,直接运行即可。
安装简介
eMD Tcl模块编译依赖于Tcl8和Swig工具,推荐使用Tcl 8.6。需要常用的MPICXX编译器(如OpenMPI),且支持C++11;如果需编译GPU加速模块,则需要CUDA 8.0+或HIP 2.0+的SDK环境。编译过程会调用Swig生成emd_wrap.cpp包装文件,这个文件最后会编译成名为emd.so的Tcl模块文件。
eMD Tcl如果需要与VMD混合使用,那么eMD和VMD应该基于同一个Tcl/Tk库(或Tcl/Tk framework)来编译,这样在Tcl/Tk库调用时不会崩溃。在MacOS X上已经提供了编译发布的App。
编译通过CMake方式,需要添加-DUSE_TCL=ON选项,进入eMD源代码根目录之后,一般编译过程为:
mkdir -p build
cd build
rm -rf *
cmake -L -DCMAKE_BUILD_TYPE=Release $(TCL_FLAGS) -DUSE_TCL=ON -DCMAKE_INSTALL_PREFIX=${EMD_ROOT} ..
make -j4 VERBOSE=1
cd ../tcl
tclsh ./mkcomp.tcl
cd emd
tclsh ./mkindex.tcl
cd ../../build
make install
${EMD_ROOT}变量指定安装目录。可以从CMake命令行传入参数定义:
- TCL_LIBRARY
- TK_LIBRARY
- TCL_INCLUDE_PATH
- TK_INCLUDE_PATH
例如使用VMD中的TCL/TK环境。
在MacOS X上也可以定义TCLTK_FRAMEWORK_PATH变量来指定Framework路径,如:
-DTCLTK_FRAMEWORK_PATH=$HOME/Applications/VMD\ 1.9.4a43-Catalina-Rev6.app/Contents/Frameworks
同时也可以组合GPU编译方式-DUSE_CUDA=ON或-DUSE_HIP=ON
mkdir -p build && cd build
rm -rf *
cmake -L -DCMAKE_BUILD_TYPE=Release -DUSE_TCL=ON -DCUDA_NVCC_FLAGS="-std=c++11;-Wno-deprecated-gpu-targets" \
-DUSE_CUDA=ON -DCMAKE_BUILD_TESTING=ON -DCMAKE_INSTALL_PREFIX=${EMD_ROOT} ..
make -j4 VERBOSE=1
make install
auto_path之后,就可以在Tclsh中通过package调用:
使用简介
eMD的Tcl模块名为emd,其中提供了多个实例文件,使用时推荐参考这些实例,这些实例覆盖大量应用场景。
- array_check.tcl 对eMD的Tcl package功能简单的测试,主要在核心数组的数据结构操作上
- lj_from_json.tcl 使用eMD模块级的JSON对象启动Lennard Jones模拟,对应Python扩展的lj_from_json.py
- lj_from_func.tcl 使用eMD函数调用方式启动Lennard Jones模拟,对应Python扩展的lj_from_func.py
- lj_from_class.tcl 通过构建eMD继承类调用方式启动Lennard Jones模拟
- lj_with_vmd.tcl 显示VMD中调用eMD进行抽样和构象绘图
- lj_create_vmd.tcl 通过eMD的配置,生成晶体结构的体系,将结果导入VMD中
导入emd库的方式有如:
在和VMD混合使用时,也可以在VMD提供的Tcl解释器终端中输入以上命令,导入emd模块。
eMD的命令都在::emd命名空间下,如以上将显示eMD的编译配置参数。eMD核心库函数被映射为Tclsh的命令语句,C++类中static函数都是以类名为前缀,如EMD类中的static函数print_config,在Tclsh中调用的格式为EMD_print_config。在EMD对象构建之前,需要生成ContextInitializer的对象,用于初始化运行环境:
set mpi_need [::emd::CMPIComm_NeedInitialize]
::emd::ContextInitializer initializer {emd} $mpi_need
if { [::emd::Comm_is_master_rank] } {
::emd::EMD_print_config
}
::emd::EMD emd_obj
$前缀。
之后就先后调用EMD对象的init和setup操作,但EMD中的Context、Comm、Particles和Sample类对象都是shared_ptr<>形式的对象变量,Tclsh并不支持shared_ptr<>类型变量的引用,故eMD的Tcl扩展中,提供返回类的原始指针的接口,如下:
set context [emd_obj get_context_ptr]
set comm [emd_obj get_comm_ptr]
set particles [emd_obj get_particles_ptr]
set sample [emd_obj get_sample_ptr]
::emd::Value val
$context init val
$comm init val
另外C++类的成员变量,需要通过cget操作获取:
JSON配置以字符串的形式给出,本身JSON对象使用{}符号,但Tcl也是通过{}将文本内容字符串化,因此看上去是嵌套的方式{{...}}:
set lat [$particles cget -lattice_list]
$lat append {{
"units" : "lj",
"type" : "fcc",
"scale" : 0.8442
}}
set reg [$particles cget -region_list]
$reg append {{
"type" : "block",
"args" : [
0, 4, 0,
4, 0, 4
]
}} [$particles cget -domain]
set trajfile [::emd::AtomFile_create emd_obj pdb]
$trajfile init {{"flush": true, "sort_tag": 1, "unwrap": 0}}
$trajfile setup
$trajfile write_file traj_out.pdb
mol new traj_out.pdb type pdb waitfor 1
$trajfile是eMD的AtomFile类对象,它可以输出构象文件,这里为traj_out.pdb,然后通过VMD的mol命令进行加载。由于这个过程在同一个内存空间,执行速度很快,需要将$trajfile的文件写出flush属性设置为true,以保证立即写入输出文件traj_out.pdb。如果在Sample对象初始化之后,通过其get_traj_ptr接口也可以获得一个AtomFile对象。emd模块提供了几个测试实例,array_check.tcl可对eMD模块进行单元测试,主要实例使用方法。执行它们可以用于测试emd模块是否正确,也可为使用参考。lj_with_vmd.tcl 提供了VMD中调用eMD进行抽样和构象绘图的实例。
这里给一个较完整的例子(见tcl/emd/lj_from_func.tcl),实现EMD主要类的初始化和运行过程,展示了Tcl中如果构建一个模拟体系,以下为脚本内容:
#!/usr/bin/tclsh
#eMD Tcl interface.
#
#Copyright (2020) Shun Xu <xushun@cnic.cn>.
#This software is distributed under the GNU General Public License.
#----------------------------------------------------------------------
#Run by:
#$ tclsh lj_from_func.tcl
#$ mpirun -n 2 tclsh ./lj_from_func.tcl
lappend auto_path [pwd]
package require emd
proc TclEMD_init_all { emd_obj } {
global context comm particles sample
set context [emd_obj get_context_ptr]
set comm [emd_obj get_comm_ptr]
set particles [emd_obj get_particles_ptr]
set sample [emd_obj get_sample_ptr]
::emd::Value val
$context init val
$comm init val
# get rank after comm.init
set comm_world [$comm cget -comm_world]
set rank [$comm_world get_rank]
# get logfile after context.init
global logfile
set logfile [::emd::Context_get_logfile ]
set lat [$particles cget -lattice_list]
$lat append {{
"units" : "lj",
"type" : "fcc",
"scale" : 0.8442
}}
set reg [$particles cget -region_list]
$reg append {{
"type" : "block",
"args" : [
0, 4, 0,
4, 0, 4
]
}} [$particles cget -domain]
$particles init {{
"position" : {
"dim" : 3,
"boundary" : ["p", "p", "p" ] ,
"create_atoms" : {
"region" : 1,
"lattice" :1,
"basis_types" : [ 1, 1, 2, 2]
}
}
,
"topology" : {
"atom_type" :[
{
"type" : "Kr",
"mass" : 1.0 }
,
[
"Ar",
1.0
]
]
}
}}
emd_obj set_force_field moly
global force_field
set force_field [emd_obj get_force_field_ptr]
$force_field init {{
"version" : 1.0,
"units" : "lj",
"type" : "moly",
"newton": true,
"ghost_vel": false}
)}
$force_field init_pair {{
"type" : "lj_cut",
"cutoff" : 2.5,
"shift" : 0,
"tail" : 0
}}
global neighbor
set neighbor [$force_field get_neighbor_ptr]
$neighbor init {{
"every_step" : 2,
"skin" : 0.3,
"pair_search" : "bin"}} [emd_obj get_particles]
$force_field print_summary $logfile
emd_obj set_mechanics mdvv
global mechanics
set mechanics [emd_obj get_mechanics_ptr]
variable nsteps 2000
$mechanics init [format {{
"run_steps": %d,
"timestep": 0.01
}} $nsteps]
$mechanics init_slot {{
"type" : "temp_berendsen",
"tstart" : 1.44,
"tstop" : 1.44,
"tau" : 0.12,
"seed" : 3434
}}
$mechanics slot_change_box
$sample init_statis {[
{
"type" : "force",
"every_step" : 100,
"fields" : [
"temp",
"epot",
"ekin",
"etotal",
"press"
] }
,
{
"type" : "domain",
"every_step" : 100,
"start_step" : 20,
"fields" : [
"vol",
"density",
"lx"
] }
]
}
$sample init_trajectory {{
"every_step": 10,
"file": "traj_out.dcd"}
}
if { $rank == 0 } {
::emd::Context_log_puts "Create trajectory AtomFile: dcd\n"
}
}
proc TclEMD_setup_all { emd_obj } {
global context comm particles sample force_field neighbor mechanics
$particles setup
$particles setup_velocity {{
"type" : "create",
"rndseed" : 87287,
"temp" : 1.44,
"dist" : "uniform",
"loop" : "geom"
}}
set pair [$force_field get_pair_ptr]
$pair set_coeff_list {[
{
"itype" : 1,
"jtype" : 1,
"epsilon" : 1,
"sigma" : 1 }
,
[ 2, 2, 1, 1 ]
]}
$pair setup
# setup pairs before bonds
$pair setup_after_topo;
$neighbor setup emd_obj
variable skin [$force_field cget -skin]
$neighbor check_box_cutoff [$particles cget -domain] $skin
$neighbor print_info 1
$comm print_node_usage
$sample setup
$mechanics setup
}
proc TclEMD_run_all { emd_obj } {
global mechanics sample
set timer [$sample get_timer_ptr]
set time_start [$timer stamp_local]
set time_cputime [::emd::Timer_cpu_time]
set s [$mechanics run 2000 [$sample cget -timer]]
$timer stamp $::emd::T_TOTAL $time_start
set cputime2 [::emd::Timer_cpu_time]
set time_end [expr $cputime2 - $time_cputime]
$timer add_elapsed $::emd::T_CPU_TIME $time_end
return $s
}
##################
set mpi_need [::emd::CMPIComm_NeedInitialize]
::emd::ContextInitializer initializer {emd} $mpi_need
if { [::emd::Comm_is_master_rank] } {
::emd::EMD_print_config
}
::emd::EMD emd_obj
TclEMD_init_all emd_obj
TclEMD_setup_all emd_obj
set i [TclEMD_run_all emd_obj ]
emd_obj final $i
emd_obj -delete
initializer -delete
与VMD交互
eMD与VMD交互是一个重要的应用,主要有两种方式: 1. Tcl package方式,eMD作为VMD的一个Plugin使用 2. IMD 交互式,eMD作为MD后端进行动力学计算过程,VMD作为MD前端进行分子结构图视和操纵。
eMD提供Tcl扩展,VMD本身也被编译成为了一个(自定义)Tcl解释器,两者在Tcl语言上交互是自然的,同时在MD建模过程也是功能互补的。eMD可以借助VMD进行计算模拟的前后和中间过程的定制化,VMD也可以借助eMD进行功能补充。提供一个操作实例见**视频**:VMD与eMD的Tcl交互。另外在VMD中Console可以导入emd package,并运行实例。如:
交互MD(Interactive MD, IMD)可以实现跨网络分子动力学操作。eMD已经实现了VMD指定的一套IMD交互协议。可提交eMD计算到远程服务器上,并开启IMD对接的IP端口;而VMD在本地开启IMD客户端服务,去访问eMD开启IMD的IP端口,从而构建一套交互式模拟过程。提供一个操作实例见**视频**:eMD+VMD=IMD。
最后修改: 2024年9月7日,