crystal_MP_Ewald.py ダウンロード/コピー

crystal_MP_Ewald.py をダウンロード

crystal_MP_Ewald.py
crystal_MP_Ewald.py
  1"""
  2エワルド法を用いて結晶のマデルングポテンシャルを計算するスクリプトです。
  3
  4概要:
  5    このスクリプトは、指定された結晶の格子パラメータとサイト情報に基づき、
  6    エワルド法を適用してマデルングポテンシャルを計算します。
  7    実空間和、逆空間和、および自己項の3つの成分を合計して、最終的なポテンシャルを求めます。
  8
  9詳細説明:
 10    プログラムは以下の手順でマデルングポテンシャルを計算します。
 11    1.  初期化された格子パラメータとサイト情報(原子の種類、電荷、位置など)を使用します。
 12    2.  コマンドライン引数からエワルドパラメータ alpha と計算精度 prec を受け取ることができます。
 13    3.  格子ベクトル、逆格子ベクトル、体積、および関連するメトリックテンソルを計算し表示します。
 14    4.  エワルドパラメータに基づき、実空間および逆空間の計算範囲(rdmax, G2max)を決定します。
 15    5.  選択された中心サイトに対する実空間和 (UC1) を計算します。この項は実空間でのクーロン相互作用を表します。
 16    6.  逆空間和 (UC2) を計算します。この項は逆格子空間でのクーロン相互作用を表し、高速フーリエ変換に似た形式です。
 17    7.  自己相互作用項 (UC3) を計算します。これは原子自身の電場による自己エネルギーを補正する項です。
 18    8.  これら3つの項を合計し、マデルングポテンシャルおよびマデルング定数をJouleとeV単位で出力します。
 19    9.  計算にかかった時間も合わせて表示されます。
 20
 21関連リンク:
 22    crystal_MP_Ewald_usage
 23    このスクリプトは tkcrystalbase.py モジュールに定義された関数を使用します。
 24"""
 25
 26import sys
 27import os
 28import time
 29from math import erf, erfc
 30from numpy import sin, cos, tan, arcsin, arccos, arctan, exp, log, log10, sqrt
 31import numpy as np
 32from numpy import linalg as la
 33import matplotlib.pyplot as plt
 34
 35from tkcrystalbase import *
 36
 37
 38pi          = 3.14159265358979323846
 39pi2         = pi + pi
 40torad       = 0.01745329251944 # rad/deg";
 41todeg       = 57.29577951472   # deg/rad";
 42basee       = 2.71828183
 43
 44h           = 6.6260755e-34    # Js";
 45h_bar       = 1.05459e-34      # "Js";
 46hbar        = h_bar
 47c           = 2.99792458e8     # m/s";
 48e           = 1.60218e-19      # C";
 49me          = 9.1093897e-31    # kg";
 50mp          = 1.6726231e-27    # kg";
 51mn          = 1.67495e-27      # kg";
 52u0          = 4.0 * 3.14*1e-7; # . "Ns<sup>2</sup>C<sup>-2</sup>";
 53e0          = 8.854418782e-12; # C<sup>2</sup>N<sup>-1</sup>m<sup>-2</sup>";
 54e2_4pie0    = 2.30711e-28      # Nm<sup>2</sup>";
 55a0          = 5.29177e-11      # m";
 56kB          = 1.380658e-23     # JK<sup>-1</sup>";
 57NA          = 6.0221367e23     # mol<sup>-1</sup>";
 58R           = 8.31451          # J/K/mol";
 59F           = 96485.3          # C/mol";
 60g           = 9.81             # m/s2";
 61
 62
 63# Lattice parameters (angstrom and degree)
 64#lattice_parameters = [ 5.62, 5.62, 5.62, 60.0, 60.0, 60.0]
 65lattice_parameters = [ 5.62, 5.62, 5.62, 90.0, 90.0, 90.0]
 66#lattice_parameters = [ 1.0, 1.0, 1.0, 90.0, 90.0, 90.0]
 67
 68# Site information (atom name, site label, atomic number, atomic mass, charge, radius, color, position)
 69sites = [
 70         ['Na', 'Na1', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.0, 0.0])]
 71        ,['Na', 'Na2', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.0, 0.5, 0.5])]
 72        ,['Na', 'Na3', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.5, 0.0, 0.5])]
 73        ,['Na', 'Na4', 11, 22.98997, +1.0, 0.7, 'red',  np.array([0.5, 0.5, 0.0])]
 74        ,['Cl', 'Cl1', 17, 35.4527,  -1.0, 1.4, 'blue', np.array([0.5, 0.0, 0.0])]
 75        ,['Cl', 'Cl2', 17, 35.4527,  -1.0, 1.4, 'blue', np.array([0.5, 0.5, 0.5])]
 76        ,['Cl', 'Cl3', 17, 35.4527,  -1.0, 1.4, 'blue', np.array([0.0, 0.0, 0.5])]
 77        ,['Cl', 'Cl4', 17, 35.4527,  -1.0, 1.4, 'blue', np.array([0.0, 0.5, 0.0])]
 78        ]
 79
 80# Minimum distance to judge idential site
 81rmin = 0.1
 82
 83# Ewald alpha parameter
 84ew_alpha = 0.3
 85
 86# Precision
 87prec = 1.0e-5
 88
 89
 90def usage():
 91    """
 92    概要:
 93        プログラムの正しい使用方法を標準出力に表示します。
 94    詳細説明:
 95        プログラムをコマンドラインから実行する際の引数のフォーマットと例を示します。
 96    """
 97    print("")
 98    print("Usage: python {} alpha prec".format(argv[0]))
 99    print("   ex: python {} {} {}".format(argv[0], ew_alpha, prec))
100    print("")
101
102def terminate():
103    """
104    概要:
105        使用方法を表示した後、プログラムを終了します。
106    詳細説明:
107        usage() 関数を呼び出し、その後にプログラムを強制終了します。
108    """
109    usage()
110    exit()
111    
112
113def main():
114    """
115    概要:
116        エワルド法によりマデルングポテンシャルを計算し、結果を表示します。
117    詳細説明:
118        この関数は、設定された格子パラメータとサイト情報に基づき、
119        エワルド法を用いて結晶のマデルングポテンシャルを計算し、その結果を標準出力に表示します。
120        具体的な計算手順は以下の通りです。
121
122        1.  設定された格子パラメータから格子ベクトル、メトリックテンソル、単位胞の体積を計算し、表示します。
123            これには cal_lattice_vectors と cal_metrics 関数が使用されます。
124        2.  逆格子パラメータ、逆格子ベクトル、逆格子メトリックテンソル、逆格子単位胞の体積を計算し、表示します。
125            これには cal_reciprocal_lattice_vectors と cal_reciprocal_lattice_parameters 関数が使用されます。
126        3.  コマンドライン引数またはデフォルト設定で指定されたエワルドパラメータ ew_alpha と計算精度 prec を表示します。
127        4.  これらのパラメータに基づき、実空間和の最大距離 rdmax と逆空間和の最大Gベクトル二乗値 G2max を決定し、
128            それぞれに対応する最大繰返し回数 nrmax および hgmax を推定し、表示します。
129        5.  実空間和 UC1 の計算を実行します。これは、中心サイトと周期的に配置された他のサイトとの間のクーロン相互作用を、
130            erfc 関数を用いて収束させた合計です。
131        6.  逆空間和 UC2 の計算を実行します。これは逆格子空間における電荷分布のフーリエ成分の相互作用の合計で、
132            Gベクトルが G2max を超えない範囲で計算されます。
133        7.  自己相互作用項 UC3 を計算します。これはエワルド法の導入により生じる、
134            原子自身の電場による自己エネルギーを補正する項です。
135        8.  各計算フェーズ(実空間和、逆空間和、合計)にかかった時間を表示します。
136        9.  計算された UC1, UC2, UC3 の3つの項を合計し、最終的なマデルングポテンシャル MP を算出します。
137        10. 算出されたマデルングポテンシャル MP をJoule単位とeV単位で表示します。
138        11. 選択された中心サイトの電荷 qi と格子定数 a を用いて、マデルング定数を計算し、表示します。
139    """
140    print("")
141    print("Lattice parameters:", lattice_parameters)
142    aij = cal_lattice_vectors(lattice_parameters)
143    print("Lattice vectors:")
144    print("  ax: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[0][0], aij[0][1], aij[0][2]))
145    print("  ay: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[1][0], aij[1][1], aij[1][2]))
146    print("  az: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(aij[2][0], aij[2][1], aij[2][2]))
147    inf = cal_metrics(lattice_parameters)
148    gij = inf['gij']
149    print("Metric tensor:")
150    print("  gij: ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[0]))
151    print("       ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[1]))
152    print("       ({:10.4g}, {:10.4g}, {:10.4g}) A".format(*gij[2]))
153    volume = cal_volume(aij)
154    print("Volume: {:12.4g} A^3".format(volume))
155
156    print("")
157    print("Unit cell volume: {:12.4g} A^3".format(volume))
158    Raij  = cal_reciprocal_lattice_vectors(aij)
159    Rlatt = cal_reciprocal_lattice_parameters(Raij)
160    Rinf  = cal_metrics(Rlatt)
161    Rgij  = Rinf['gij']
162    print("Reciprocal lattice parameters:", Rlatt)
163    print("Reciprocal lattice vectors:")
164    print("  Rax: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[0]))
165    print("  Ray: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[1]))
166    print("  Raz: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Raij[2]))
167    print("Reciprocal lattice metric tensor:")
168    print("  Rgij: ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[0]))
169    print("        ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[1]))
170    print("        ({:10.4g}, {:10.4g}, {:10.4g}) A^-1".format(*Rgij[2]))
171    Rvolume = cal_volume(Raij)
172    print("Reciprocal unit cell volume: {:12.4g} A^-3".format(Rvolume))
173
174    nsites = len(sites)
175    
176    print("")
177    print("Ewald parameters")
178    print("  alpha:", ew_alpha)
179    norder = -log10(prec)
180    print("  precision = {} = 10^-{}".format(prec, norder))
181
182    rdmax     = (2.26 + 0.26 * norder) / ew_alpha
183    erfc_rdmax = erfc(ew_alpha * rdmax)
184    print("  RDmax = {} A, where erfc(alpha*RDmax) = {}".format(rdmax, erfc_rdmax));
185
186    lsin  = np.empty(3, dtype = float)
187    nrmax = np.empty(3, dtype = int)
188    lsin[0] = sin(torad * lattice_parameters[3])
189    lsin[1] = sin(torad * lattice_parameters[4])
190    lsin[2] = sin(torad * lattice_parameters[5])
191    nrmax[0] = int(rdmax / sqrt(gij[0][0] * lsin[1] * lsin[2])) + 1
192    nrmax[1] = int(rdmax / sqrt(gij[1][1] * lsin[2] * lsin[0])) + 1
193    nrmax[2] = int(rdmax / sqrt(gij[2][2] * lsin[0] * lsin[1])) + 1
194    print("  nrmax:", *nrmax)
195
196    cal_N = int(4.0 / 3.0 * pi * rdmax**3 / volume * nsites)
197    print("  cal_N(real):", cal_N)
198
199    G2max = ew_alpha**2 / pi**2 * (-log(prec))
200    print("  G2max:", G2max)
201    print("      exp(-pi^2 * G2max^2 / alpha^2) = ", exp(-pi**2 * G2max**2 / ew_alpha**2))
202    lsin[0] = sin(torad * Rlatt[3])
203    lsin[1] = sin(torad * Rlatt[4])
204    lsin[2] = sin(torad * Rlatt[5])
205    hgmax = np.empty(3, dtype = int)
206    hgmax[0] = int(sqrt(G2max / (Rgij[0][0] * lsin[1] * lsin[2]))) + 1
207    hgmax[1] = int(sqrt(G2max / (Rgij[1][1] * lsin[0] * lsin[2]))) + 1
208    hgmax[2] = int(sqrt(G2max / (Rgij[2][2] * lsin[0] * lsin[1]))) + 1
209    print("  hgmax:", *hgmax)
210
211    cal_N = int(4.0 / 3.0 * pi * G2max**1.5 / Rvolume * nsites)
212    print("  cal_N(reciprocal):", cal_N)
213
214    namei, labeli, zi, Mi, qi, ri, colori, pos_i = sites[0]
215
216    stime1 = time.time()
217    UC1 = 0.0
218    for iz in range(-nrmax[2], nrmax[2]+1):
219     for iy in range(-nrmax[1], nrmax[1]+1):
220      for ix in range(-nrmax[0], nrmax[0]+1):
221        for j in range(nsites):
222            namej, labelj, zj, Mj, qj, rj, colorj, pos_j = sites[j]
223            rij  = distance(pos_i, pos_j + np.array([ix, iy, iz]), gij)
224            
225            if rij < rmin:
226                 continue
227
228            erfcar = erfc(ew_alpha * rij)
229            UC1 += qj * erfcar / (rij * 1.0e-10)   # in eV
230    etime1 = time.time()
231
232    origin = np.array([0.0, 0.0, 0.0])
233    UC2 = 0.0
234    Kexp = pi * pi / ew_alpha / ew_alpha
235    Krec = 1.0 / pi / (volume * 1.0e-30)
236#    for l in range(-hgmax[2], hgmax[2]+1):
237    for l in range(0, hgmax[2]+1):
238     for k in range(-hgmax[1], hgmax[1]+1):
239      for h in range(-hgmax[0], hgmax[0]+1):
240          G2 = distance2(origin, np.array([h, k, l]), Rgij)
241          if G2 == 0.0 or G2 > G2max:
242              continue
243
244          phi_i  = pi2 * (h * pos_i[0] + k * pos_i[1] + l * pos_i[2])
245          cosphi_i = cos(phi_i)
246          sinphi_i = sin(phi_i)
247
248          cossum_j = 0.0
249          sinsum_j = 0.0
250          for j in range(nsites):
251              namej, labelj, zj, Mj, qj, rj, colorj, pos_j = sites[j]
252
253              phi_j = pi2 * (h * pos_j[0] + k * pos_j[1] + l * pos_j[2])
254              cossum_j += qj * cos(phi_j)
255              sinsum_j += qj * sin(phi_j)
256
257          fcal = cosphi_i * cossum_j + sinphi_i * sinsum_j
258          if l != 0:
259            fcal *= 2.0
260          expg = exp(-Kexp * G2) / (G2 * 1.0e+20)
261          UC2 += Krec * expg * fcal
262    etime2 = time.time()
263
264    UC3 = qi * 2.0 * (ew_alpha * 1.0e10) / sqrt(pi)
265
266    MP = UC1 + UC2 - UC3
267    etime3 = time.time()
268
269# Coefficient to calculate electrostatic potential
270    Ke = e * e / 4.0 / pi / e0
271
272    print("")
273    print("Time for real space sum     : {:6}".format(etime1 - stime1))
274    print("Time for real reciprocal sum: {:6}".format(etime2 - etime1))
275    print("Total time                  : {:6}".format(etime3 - stime1))
276    
277    print("  Madelung potential: {:12.6g} J  (= {:12.6g} + {:12.6g} - {:12.6g})".format(Ke * MP, Ke * UC1, Ke * UC2, Ke * UC3))
278    print("  Madelung potential: {:12.6g} eV (= {:12.6g} + {:12.6g} - {:12.6g})".format(Ke / e * MP, Ke / e * UC1, Ke / e * UC2, Ke / e * UC3))
279# Charge is represented by q0 to define Madelung constant
280# Lattice parameter a is represented by q0 to define Madelung constant    
281    print("  Madelung constant: {:14.8g}".format(0.5 * MP / abs(qi) * (lattice_parameters[0] * 1.0e-10)))
282
283
284    terminate()
285
286
287if __name__ == '__main__':
288    argv = sys.argv
289    narg = len(argv)
290    if narg >= 2:
291        ew_alpha = float(argv[1])
292    if narg >= 3:
293        prec = float(argv[2])
294
295    main()