ある種の位相的場の理論(Cohomological Field Theory)の"分配関数"(数学では、descendant potentialという呼び方をする時もある)は
(1)何らかの可積分階層のτ関数になっている
(2)Virasoro constraintsや、より一般にW-constraintsと呼ばれる(通常、線形偏微分方程式系で書かれる)条件を満たす
(3)対数は"free energy"で、その無分散極限はWDVV方程式を満たす(prepotential)
(4)"free energy"部分は、種数展開が出来て、それぞれ種数gのGromov-Witten不変量の母関数になっている(種数0部分はprepotential)
という性質を持つということが経験的に分かってるっぽい。幾何学的にはGW不変量の母関数である一方、代数的にみると、Virasoro代数やW代数は、可積分階層のnon-isospectral symmetryとして現れ、Virasoro/W-constraintsは、"大体"最高ウェイト条件となっている。全く異なる由来を持つ2つの量が一致するということで、面白いのだけど、そうなる理由は分からない


具体例としては(予想も含めると)
・Kontsevich-Witten tau function(二次元量子重力理論の分配関数らしい。KdV階層の解)
・Hurwitz tau function(数学的に発見されて、物理的な模型の分配関数として作られたわけではないっぽい。KP階層の解)
・double Hurwitz numberの母関数(戸田階層の解)
・位相的極小模型の分配関数(2D topological minimal matter+topological gravityと解釈できるらしい。Gelfand-Dikii階層/Drinfeld-Sokolov階層)
・位相的シグマ模型の分配関数(よく知らないのだけど、計算できる例は限られていて、射影空間とかtoric Calabi-Yauとかだと分かるっぽい?旗多様体は?)
・様々な行列模型の分配関数
・Nekrasov分配関数?(まぁ、全然知らない)
・2D large N pure SU(N) Yang-Mills理論の分配関数?(Migdalの公式は、無限個の時間変数を含まないので、何か"変形"が必要)
あたり。必ずしも、"位相的"とは思われてなかった理論も含まれる。


1990年代以降、"数理物理"(ミラー対称性とかSW理論)は、幾何学の言葉で書かれることが増えて、1980年代の話題(ソリトン,CFT,可解格子模型)と断絶してしまった感がある(CFTのことを知らない幾何学の人も結構いるらしい)けど、ちゃんと連続した話ではある

コヒーレント状態の有限群での類似物

1970年頃、PerelomovやGilmoreたちが、一般のLie群に対する、generalized coherent stateという概念を考えたのだけど、有限群の場合にも、コヒーレント状態の類似物を考えられるっぽい

参考)coherent state物語
http://d.hatena.ne.jp/m-a-o/20130303#p2


v_1 = (1,0), v_2 = (-1/2 , √3/2) , v_3 = (-1/2 , -√3/2)と、w=(x,y)に対して
w= c_1 v_1 + c_2 v_2 + c_3 v_3
の解(c_1,c_2,c_3)を考える。この解は無数にあるけど、擬似逆行列によって、"自然な"解
(c_1 , c_2 , c_3) = (\frac{2x}{3} ,-\frac{1}{3}x + \frac{1}{\sqrt{3}}y , -\frac{1}{3}x - \frac{1}{\sqrt{3}}y)
がある。この解は
w = \frac{2}{3} \langle w,v_1 \rangle v_1 + \frac{2}{3} \langle w , v_2 \rangle v_2 + \frac{2}{3} \langle w , v_3 \rangle v_3
とも書ける。こっちの方が、計算量的にも計算精度的にも優れてるけど、普通は、こういう関係は成立しなくて、こういう風に書ける理由は
 v_1^T v_1 + v_2^T v_2 + v_3^T v_3 = \frac{3}{2} I
という式にある(擬似逆行列の言葉でみれば、転置行列が定数倍を除いて、擬似逆行列に一致するという条件と同じ)。これが、調和振動子のコヒーレント状態とかで、よくみかける式
 \frac{1}{\pi} \int | \alpha \rangle \langle \alpha| d\alpha = I
の類似物(resolution of the identityとか呼ばれる式)


Lie群のコヒーレント状態では、Lie群が、コヒーレント状態の集合に推移的に作用していて、過剰完全基底をなしていた。今の場合、v_1,v_2,v_3は、平面上の120度回転で互いに移りあう。Lie群の場合は、既約ユニタリ表現の最高ウェイトベクトルに群を作用させて、過剰完全基底を得ていたけども、有限群の表現では、最高ウェイトベクトルに相当するものがなく、特に、どのベクトルを選んでもいいらしい(連続ウェーブレット変換のマザーウェーブレットと似たような状況)


有限群の表現は、何でもいいというわけではなくて、既約表現である必要がある。上の3次巡回群の表現は、複素数体上では既約でないけども、実数体上では既約になっている。有限群Gの(適当な体上の)既約表現(ρ,V)で、表現空間には内積(複素表現の場合にはユニタリ内積)が入ってるとして、規格化されたベクトルvを取って、軌道G(v)={ρ(g)v | g ∈ G}を考える。この時、G(v)は、Vの生成系となっている。何故なら、G(v)が生成するベクトル空間Wは、Vの不変部分空間であるけども、(ρ,V)の既約性より、Wは{0}かV自身で、vは0でないので、W=Vとなる。既約でないためにダメな例として、2次巡回群が、xy平面上に鏡映(x,y)->(x,-y)によって作用しているとすると、vの選び方によっては、G(v)が生成する不変部分空間は、一次元になってしまう


G(v)自体は、複数回同じ点を通る場合もあるけど、少し試すと、有限群Gと有限次元既約表現(ρ,V)について、vを長さ1のベクトルとすると、任意のベクトルwに対して
 w = \frac{dim(V)}{|G|} \sum_{g \in G} \langle w , \rho(g)v \rangle \rho(g)v
が成り立つという予想が立つ。これは、"有限ウェーブレット変換"とか呼んでもいいような代物だと思う


もう一つ、殆ど同じことではあるけど、wに対して、係数\langle w , \rho(g)v \rangleを対応させる上の計算が、一般に擬似逆行列による計算と一致することを言いたい。これは
 \frac{dim(V)}{|G|}\sum_{g \in G} \langle e_i , \rho(g)v \rangle \langle \rho(g)v , e_j \rangle = \delta_{ij}
を示せば、両方一気に言える(e_iは正規直交基底)


証明のアウトラインは以下の通り
\sum_{g \in G} |\rho(g)v \rangle \langle \rho(g)v|
が、VからVへの恒等写像の定数倍であるということは、intertwinerであるということなので、逆にこいつがintertwinerであることを言えば、(ρ,V)の既約性とSchurの補題から定数倍の因子を除いて、恒等写像に一致することは分かる。けど、これだと定数倍の因子が出ない。今、vを正規直交基底の一つとすると、この作用素は、vを別の基底ベクトルに取り換えても同じになるので、
\sum_{g \in G} |\rho(g)v \rangle \langle \rho(g)v| = \sum_{g \in G} \frac{1}{dim(V)} \sum_{k} |\rho(g)e_k \rangle \langle \rho(g)e_k| = \frac{|G|}{dim(V)}I
となって、 定数倍の因子まで決定できる



Gabor変換や連続Wavelet変換は、最小2-normを与えるという性質で特徴づけられ、擬似逆行列は(解が無数にある不定系に対して)最小ノルム解を与えるという特徴づけがあって、両者は、次元が無限か有限かということを除けば、本質的に同じものであるように見えた。のだけど、両者は、そこらに落ちてる説明を読んでも、見掛け上同じ計算をしているようには見えないので、ちゃんと同じに見えるようにしようと思ったのが、こういうことを考えた動機。なので、別に何か応用があるというわけではない。


まぁ、分かってみると当たり前のことのようではあるけど、要するに対称性がある特殊な"基底"(通常一次独立でないので、生成系とか呼ぶ方が正しい気がするけど)を使う場合には、擬似逆行列(最小ノルム解)を求める時に計算量が大きく減らせる(上に、多分計算精度も高い)というのがGabor変換etc.の大きな意義だということが分かる。一方で、工学的な問題で、そんな対称性の高い生成系を用いるのは、本当に正しいことだろうかという気もする

エルマンネットワーク

というのを実装して見た。リカレントニューラルネットワーク(RNN)の一形態?

単純再帰型ネットワーク
http://www.cis.twcu.ac.jp/~asakawa/waseda2002/elman.pdf

import numpy as np

def sigmoid(x):
     return np.tanh(x)

def dsigmoid(x):
     return 1.0-x*x

class ElmanNet:
    def __init__(self , numIn , numHidden , numOut):
       self.input = np.ones( numIn )
       self.output = np.ones( numOut )
       self.hidden = np.ones( numHidden )
       self.context = np.zeros( numHidden )
       self.weight_in_hid = np.random.uniform(low=-0.5,high=0.5,size=(numIn , numHidden) )
       self.weight_hid_out = np.random.uniform(low=-0.5,high=0.5,size=(numHidden,numOut) )
       self.weight_cont_hid = np.random.uniform(low=-0.5,high=0.5,size=(numHidden,numHidden) )
    def propagate_forward(self , data):
       self.input = np.copy(data)
       self.context = np.copy(self.hidden)
       self.hidden = np.vectorize(sigmoid)(np.dot(self.input , self.weight_in_hid) + np.dot(self.context , self.weight_cont_hid))
       self.output = np.vectorize(sigmoid)(np.dot(self.hidden , self.weight_hid_out))
       return self.output
    def propagate_backward(self , target , learnRate=0.1):
       error_out = (target - self.output) * np.vectorize(dsigmoid)(self.output)
       error_hidden = np.dot(error_out , self.weight_hid_out.T) * np.vectorize(dsigmoid)(self.hidden)
       self.weight_hid_out  += np.array( learnRate * np.matrix(self.hidden).T * np.matrix(error_out) )
       self.weight_in_hid   += np.array( learnRate * np.matrix(self.input).T * np.matrix(error_hidden) )
       self.weight_cont_hid += np.array( learnRate * np.matrix(self.context).T * np.matrix(error_hidden) )
    def train(self , samples , r=1500):
       L = len(samples)
       for _ in xrange(r):
           for (inp,outp) in samples:
               self.propagate_forward(inp)
               self.propagate_backward(outp)
    def predict(self , data):
       return self.propagate_forward(data)


if __name__=="__main__":
   def tobit(n,nbits=6):
      return [int(n==i) for i in xrange(nbits)]
   net = ElmanNet(6,15,6)
   samples = [(tobit(0),tobit(1)),(tobit(1),tobit(2)),(tobit(2),tobit(3)),(tobit(3),tobit(4)),
              (tobit(4),tobit(5)),(tobit(5),tobit(4)),(tobit(4),tobit(3)),(tobit(3),tobit(2)),
              (tobit(2),tobit(1)),(tobit(1),tobit(0)),(tobit(0),tobit(1)),(tobit(1),tobit(2)),
              (tobit(2),tobit(3)),(tobit(3),tobit(4)),(tobit(4),tobit(5)),(tobit(5),tobit(4))]
   net.train(samples)
   out = net.predict(tobit(4))
   print(out)
   out = net.predict(out)
   print(out)
   print net.predict(tobit(2))

例として、0,1,2,3,4,5,4,3,2,1,0,1,2,3,4,5,...という時系列を学習させている。一応、値が上昇中か下降中か覚えておく必要があるけど、勝手に覚えておいてくれるという寸法。0,1,2,3,4,5は、[1,0,0,0,0,0],[0,1,0,0,0,0],[0,0,1,0,0,0],...にエンコードしてる。3bitで[0,0,0],[0,0,1],[0,1,],[0,1,1],...とかやった方がメモリには優しいけど、予測精度がでない


一応、上の計算結果は(乱数で初期化している部分があるので、結果は毎回異なるけど)例えば

[ 0.05445429 -0.0483922  -0.14895301  0.98319809  0.09161327 -0.09690022]
[ 0.05653943  0.13345083  0.98843438 -0.28019387 -0.08493135  0.05609208]
[-0.09952735  0.98482479  0.22479163 -0.13254797 -0.08240431  0.20785037]

とかなって、[0,0,0,1,0,0],[0,0,1,0,0,0],[0,1,0,0,0,0]に近い値が返ってくる


有限オートマトンとか、ある種の文法構造を学習することができるっぽいけど、他の文法推論アルゴリズムと比べて、大規模な文法の推論には向かないらしい

アルゴンの分子動力学計算

やったことなかったので、アルゴンの分子動力学計算をやってみた。まぁ、ただの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がよくない?)
・固体から加熱していった時と、気体から冷却していった時で、融点・沸点が大きくずれる理由は何?経験的に知られているように、固体の液化温度と液体の固化温度が概ね一致するための条件は?