アルゴンの分子動力学計算
やったことなかったので、アルゴンの分子動力学計算をやってみた。まぁ、ただのGromacsの使い方メモのような。使用したGromacsのバージョンは、4.6.1
原理的なことに関しては、
分子動力学シミュレーションにおける温度・圧力制御第1回=能勢の熱浴と能勢・フーバー熱浴
https://www.jstage.jst.go.jp/article/mssj1998/10/44/10_44_29/_article/-char/ja/
分子動力学シミュレーションにおける温度・圧力制御 第3回:速度スケーリング法,ガウス束縛法,ベレンゼン熱浴
https://www.jstage.jst.go.jp/article/mssj/11/2/11_2_2_43/_article/-char/ja/
あたりを見ると、大体書いてある。まぁ、MDは原理自体は難しいことは、あまりないと思う。
ポテンシャルは、Lennard-Jonesポテンシャルで、あまり関係ないけど
量子力学はLennard-Jonesポテンシャルを再現できるか
http://d.hatena.ne.jp/m-a-o/20121217#p2
も参照。きっと、2030年くらいになれば、怪しい近似ポテンシャルとか力場とか使わなくても、量子力学的に原子核間に働くヘルマン・ファインマン力を計算して、原子核を古典論的に時間発展させるくらいのことは、そこらのPCで出来るようになるかもしれないけど、現在のところ厳しそう
(1)トポロジーファイルというものを作る。今の場合、実質的には、Lennard-Jonesパラメータと原子数の指定くらいしかやってない
;; argon.top [ defaults ] ; nbfuncは非結合粒子間ポテンシャルで、LJポテンシャルかBuckinghamポテンシャルを指定する(LJなら、1) ; comb-ruleは、異原子間のLJパラメータとかの決め方(sigma/epsilonを指定する場合は、2か3)。今はどっちでも同じ ; nbfunc comb-rule gen-pairs fudgeLJ fudgeQQ 1 3 no 1.0 1.0 [ atomtypes ] ; name mass charge ptype sigma(nm) epsilon(kJ/mol) AR 39.948 0.0 A 0.34 0.99774 [ moleculetype ] ; name nrexcl Ar 1 [ atoms ] ; id type resnr residu atom cgnr charge 1 AR 1 Ar Ar 1 0 [ system ] Argon [ molecules ] AR 512
(2)MD計算の初期状態を指定する。
pdbファイルで指定できるけど、その場合は、初速度の指定はできない(後で、Gromacsが適当な速度を付加してくれる)
速度も指定したい場合は、.groファイルフォーマットというのを使えばいいっぽい
とりあえず、何も考えず、立方格子の頂点に原子を配置するpdbを生成するコードをpythonで作る
#-*- coding:utf-8 -*- #CellSizeの単位は(Å) def gen_cubic_pdb(CellSize , N): L = CellSize A = CellSize/N print('HEADER argon') print('CRYST1 %9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d' % (L,L,L,90.0,90.0,90.0,"P 1",1)) atom_format = "ATOM %(serial)5d %(name)-4s %(resName)-4s%(chainID)1s%(resSeq)4d%(iCode)1s %(x)8.3f%(y)8.3f%(z)8.3f%(occupancy)6.2f%(tempFactor)6.2f" for nx in xrange(N): for ny in xrange(N): for nz in xrange(N): vars = dict() vars['serial'] = nx*N*N+ny*N+nz+1 vars['x'] = nx * A vars['y'] = ny * A vars['z'] = nz * A vars['name'] = 'Ar' vars['resName'] = 'Ar' vars['resSeq'] = nx*N*N+ny*N+nz+1 vars['iCode'] = '' vars['occupancy'] = 1.0 vars['chainID'] = 'A' vars['tempFactor'] = 0.0 print(atom_format % vars) if __name__=="__main__": gen_cubic_pdb(250.0 , 8)
python genpdb.py > init.pdb
とかで、初期状態を指定する。トポロジーファイルに原子数を指定してあるので、一致させる
(3)MDの計算条件の指定(タイムステップやステップ数や相互作用のカットオフ等)
指定できる項目は、凄く多い
http://www.gromacs.org/Documentation/File_Formats/.mdp_File
とりあえず、最小に近い記述は、以下の通り(温度を273Kに保つという以外は、技術的な詳細)
;; NVT.mdp ; RUN CONTROL PARAMETERS integrator = md dt = 0.005 ;; timestep(ps) 5fs nsteps = 200000 ;; total time=1000ps=1ns ; Output frequency and precision for xtc file nstxtcout = 1000 xtc-precision = 1000 ; Neighbour search algorithm (simple or grid) ns-type = grid nstlist = 1 ;; nblist update frequency ; Apply long range dispersion corrections for Energy and Pressure DispCorr = EnerPres ; generate velocities gen-vel = yes gen-temp = 273.0 ; Temperature coupling: no(NVE) , nose-hoover , berendsen tcoupl = berendsen tc-grps = system tau-t = 0.2 ;; time constant(ps) ref-t = 273.0 ;;reference temperature(K)
(4)MD計算
以上の入力ファイルを、まとめて、gromppというのにかけると、単一の入力ファイルを作ってくれる
grompp -p argon.top -c init.pdb -f NVT.mdp -o md.tpr
md.tprができれば成功。で、あとはmdrun
mdrun -deffnm md
出力は
md.xtc : trajectory
md.edr : エネルギー
md.gro : 最終的な位置と速度(継続して、別の計算に投げる時とかに使う)
md.log : ログファイル
など
(5)計算結果の確認と解析
g_energy -f md.edr -o energy -xvg none
とかやって、
End your selection with an empty line or a zero. ------------------------------------------------------------------- 1 LJ-(SR) 2 Disper.-corr. 3 Coulomb-(SR) 4 Potential 5 Kinetic-En. 6 Total-Energy 7 Temperature 8 Pres.-DC 9 Pressure 10 Vir-XX 11 Vir-XY 12 Vir-XZ 13 Vir-YX 14 Vir-YY 15 Vir-YZ 16 Vir-ZX 17 Vir-ZY 18 Vir-ZZ 19 Pres-XX 20 Pres-XY 21 Pres-XZ 22 Pres-YX 23 Pres-YY 24 Pres-YZ 25 Pres-ZX 26 Pres-ZY 27 Pres-ZZ 28 #Surf*SurfTen 29 T-System 30 Lamb-System
などと出てきたら、適当に
4 5 6 7 9
と打つと、energy.xvgというファイルが作られて、一定ステップごとに、ポテンシャルエネルギー・運動エネルギー・全エネルギー・温度・圧力の時間変化が見れる。とりあえず、温度が大体273Kに保たれていることとかが見て取れる。あと
Energy Average Err.Est. RMSD Tot-Drift ------------------------------------------------------------------------------- Potential -4.0542 0.18 1.72151 -0.99296 (kJ/mol) Kinetic En. 1739.87 0.031 1.07103 -0.172205 (kJ/mol) Total Energy 1735.81 0.2 1.36278 -1.1652 (kJ/mol) Temperature 273.003 0.0049 0.168055 -0.027022 (K) Pressure 1.23123 0.00017 0.00798183 -0.000493924 (bar)
とかいう平均値が出る。NVTの値から、理想気体の状態方程式によって、圧力を見積もると
p = NkT/V = 512*(1.3806488*1e-23)*273/(250*1e-10)^3 = 123508.202...(Pa)=1.23508...(bar)
くらいなので、大体合ってそう。Err.Est.が凄く小さいのは、多分最初からほぼ平衡状態にあることを示していると思う
MD計算で分かる物理量は、拡散係数、粘性係数(ずり粘性率、体積粘性率)、熱伝導率、比熱、密度、沸点、融点とか色々あると思うけど、一番簡単なのは、自己拡散係数で、平均二乗変位から見積もる方法と、速度相関関数からGreen-Kubo公式で求める方法があって、前者は、Gromacsに付いてるg_msdで簡単に計算できて
g_msd -s md.tpr -f md.xtc -o msd -xvg none
とかやって、適当に"0"と打つと、D=1.086e+04 (+/- 2985) 1e-5 cm^2/sであるとか返ってくる。
アルゴンガスの自己拡散係数のデータは探しても、あまり出てこないけど
DIFFUSION COEFFICIENT@thermopedia
http://www.thermopedia.com/content/696/
によると、常温常圧で、0.157(cm^2/s)とある。今、MD計算の方は、圧力を常圧にしてない(NPTアンサンブルでの計算は(6)を参照)けど、オーダーは合ってそう
g_msd -s md.tpr -f md.xtc -o msd -xvg none -beginfit 100 -endfit 600
とかやると、100psから600psのデータを使って自己拡散係数を出してくれるッぽい(今の場合、デフォルトでは100~900ps)。計算値は
100~400ps : 8346.9463 (+/- 2933.8926) 1e-5 cm^2/s 100~600ps : 9714.9385 (+/- 3332.4248) 1e-5 cm^2/s 100~800ps : 1.057e+04 (+/- 3164) 1e-5 cm^2/s 100~900ps : 1.086e+04 (+/- 2985) 1e-5 cm^2/s
1ns程度だと、まだ若干収束してない感じがする。計算時間を2nsと4nsに増やすと、それぞれ、1.242e+04 (+/- 1964) 1e-5 cm^2/sと1.287e+04 (+/- 166.5) 1e-5 cm^2/sくらいになって、標準偏差も大分小さくなるので、0.13(cm^2/s)弱くらいで収束するっぽい
Green-Kubo公式による自己拡散係数の計算もGromacsに付いているg_velaccとg_analyzeというので、できるらしい。
Diffusion Constant
http://www.gromacs.org/Documentation/How-tos/Diffusion_Constant
g_velaccというので、自己速度相関関数を計算してくれるっぽい。けど、そのためには中間速度を出力させる必要があって、それには、NVT.mdpに次の3行を追加して計算する。こうすると、md.trrとかいうファイルができるはず
nstxout = 100 nstvout = 100 nstfout = 100
計算できたら、
g_velacc -nonormalize -mol -f md.trr -s md.tpr g_analyze -f vac.xvg -integrate
とかで
Calculating the integral using the trapezium rule Integral 1 34.11745 +/- 0.00000
というのが出てくる。単位は、(nm^2/ps)らしいので、(cm^2/s)に直すためには、10^(-2)をかけて、これは速度自己相関関数の積分値なので、拡散係数にするために、3で割ると、0.1137(cm^2/s)という値になる。
(6)沸点・融点の計算
今度は、NPT一定の条件で、ちょっとずつ温度を下げていって、気液相転移・固液相転移を見る。歴史的には、Alderが、2次元系で剛体球ポテンシャルで、固液相転移を確認したのが、分子動力学の始まりらしい。
topファイルとpdbファイルは、さっきと同じで、mdpを以下のように変える
;; anneal.mdp ; RUN CONTROL PARAMETERS integrator = md dt = 0.001 ;; timestep(ps) 1fs nsteps = 40000000 ;; total time=40ns ; Output frequency and precision for xtc file nstenergy = 10000 nstlog = 10000 nstxtcout = 10000 xtc-precision = 1000 ; Neighbour search algorithm (simple or grid) ns-type = grid nstlist = 1 ;; nblist update frequency ; Apply long range dispersion corrections for Energy and Pressure DispCorr = EnerPres ; generate velocities gen-vel = yes gen-temp = 150.0 ;; temperature and pressure coupling tcoupl = berendsen tc-grps = system tau_t = 0.2 ;; time constant(ps) ref_t = 150.0 ;;reference temperature(K) pcoupl = berendsen pcoupltype = isotropic nstpcouple = 1 tau_p = 0.015 ;; time constant(ps) compressibility = 4.5e-5 ;; (1/bar) ref_p = 1.0 ;;reference pressure(bar) ; SIMULATED ANNEALING annealing = single ; Type of annealing for each temperature group (no/single/periodic) annealing_npoints = 11 ; Number of time points annealing_time = 0 2800 3000 4800 5000 14800 15000 24800 25000 34800 35000 ; List of times(ps) at the annealing points annealing_temp = 150 150 110 110 90 90 70 70 50 50 30 ; Temp. at each annealing point
このように書くと、0~2800psでは、熱浴の温度を150Kに保ち、2800~3000psの間に、150Kから110Kへ冷却し(熱浴の温度をリニアに下げていく)、3000~4800psでは、温度を110Kに保ち...という感じの計算をやってくれる。圧力も1barに保つように設定されている。で、以下のコマンドで計算
grompp -p argon.top -c init.pdb -f anneal.mdp -o md.tpr mdrun -deffnm md
計算が終わると、今度はg_energyコマンドで、密度や体積も出力できるようになっている。各温度で平衡状態に於ける密度を見ると
150K:3.1(kg/m^3) 70K:7.4(kg/m^3) 50K:1690(kg/m^3) 30K:1742(kg/m^3)
くらいになっていて、70~50Kの間で、密度の急激な増大が起きていることが確認できる。実験値は
気体アルゴン密度(0℃、1気圧):1.784(kg/m^3) 液体アルゴン密度(87K、液体):1380(kg/m^3) 固体アルゴン密度(40K、固体):1656(kg/m^3) 融点:84K 沸点:87K
なので、融点・沸点は全然ずれている。密度は、大体合っているっぽい。一応、最終配置から、30Kに保って、自己拡散係数を出してみると、液体でなく、固体になっているっぽい
逆に、固体から始めて温度をあげていく。以下のようなpythonコードで、fcc結晶のpdbを作成する。今度は、原子数は3x3x3x4=108個
#!/usr/bin/env python def gen_fcc(a , Nx , Ny , Nz): ret = set([]) for n1 in xrange(2*Nx): for n2 in xrange(2*Ny): for n3 in xrange(2*Nz): if (n1+n2+n3)%2==1:continue x = n1*0.5*a y = n2*0.5*a z = n3*0.5*a yield (x,y,z) if __name__=="__main__": a = 5.26 #-- lattice constant Nx,Ny,Nz = 3,3,3 print('HEADER argon') print('CRYST1 %9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d' % (Nx*a,Ny*a,Nz*a,90.0,90.0,90.0,"P 1",1)) atom_format = "ATOM %(serial)5d %(name)-4s %(resName)-4s%(chainID)1s%(resSeq)4d%(iCode)1s %(x)8.3f%(y)8.3f%(z)8.3f%(occupancy)6.2f%(tempFactor)6.2f" for n,(x,y,z) in enumerate(gen_fcc(a,Nx,Ny,Nz)): vars = dict() vars['serial'] = n+1 vars['x'] = x vars['y'] = y vars['z'] = z vars['name'] = 'Ar' vars['resName'] = 'Ar' vars['resSeq'] = n+1 vars['iCode'] = '' vars['occupancy'] = 1.0 vars['chainID'] = 'A' vars['tempFactor'] = 0.0 print(atom_format % vars)
.mdpは以下のように適当に作った(.topファイルの原子数も108に変更しておくこと)
;; heatup.mdp ; RUN CONTROL PARAMETERS integrator = md dt = 0.001 ;; timestep(ps) 1fs nsteps = 40000000 ;; total time=40ns ; Output frequency and precision for xtc file nstenergy = 100000 nstlog = 100000 nstxtcout = 100000 xtc-precision = 1000 ; Neighbour search algorithm (simple or grid) ns-type = grid nstlist = 1 ;; nblist update frequency rlist = 0.5 ;;(nm) rcoulomb = 0.5 rvdw = 0.5 ; Apply long range dispersion corrections for Energy and Pressure DispCorr = EnerPres ; generate velocities gen-vel = yes gen-temp = 50.0 ;; temperature and pressure coupling tcoupl = berendsen tc-grps = system tau_t = 0.2 ;; time constant(ps) ref_t = 50.0 ;;reference temperature(K) pcoupl = berendsen pcoupltype = isotropic nstpcouple = 1 tau_p = 0.015 ;; time constant(ps) compressibility = 4.5e-5 ;; (1/bar) ref_p = 1.0 ;;reference pressure(bar) ; SIMULATED ANNEALING annealing = single ; Type of annealing for each temperature group (no/single/periodic) annealing_npoints = 11 ; Number of time points annealing_time = 0 2800 3000 4800 5000 14800 15000 24800 25000 34800 35000 ; List of times(ps) at the annealing points annealing_temp = 50 50 60 60 80 80 100 100 120 120 160 ; Temp. at each annealing point
今度は、平衡状態に達した後で、各温度での密度が
50K:1750(kg/m^3) 80K:1680(kg/m^3) 100K:1620(kg/m^3) 120K:1290(kg/m^3) 160K:3.01(kg/m^3)
くらいになっているので、100~120Kで液体化して、120~160Kの間で気体化しているっぽい。厳密には、もっと細かく点を取るべきだと思うけど、冷却していった時と、加熱していった時で、沸点と融点が全然合ってそうにないのは、何故だろう?一応、これらの値は、原子数を500に増やしても、ほぼ同一だった
(7)疑問
心当たりのある方は、教えてください
・沸点や融点が実験値とかなりずれる理由は何?(単純に、ポテンシャルのfittingがよくない?)
・固体から加熱していった時と、気体から冷却していった時で、融点・沸点が大きくずれる理由は何?経験的に知られているように、固体の液化温度と液体の固化温度が概ね一致するための条件は?