-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
155 lines (136 loc) · 5.72 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import sys
import copy
from io import xsf_info, el_info
from io import qe_out_info, make_qe_in
from io import init_log, upd_log
from io import init_axsf, upd_axsf
from mc import mc
from bv import bv
import os
import numpy as np
import subprocess
####################
# READ INPUT FILES #
####################
xsf_filename = sys.argv[1] # read xsf filename
el_filename = sys.argv[2] # read element list filename
#############################
# SET SIMULATION PARAMETERS #
#############################
niter = 1000
max_disp = 0.0 # angstroms
T_move = 500 # kelvin
ry_ev = 13.605693009
bohr_ang = 0.52917721067
buf_len = 2.5 # length above surface within which atoms can be added
#mu_list = [-989.926, -428.156] # ag, o - p/p0 = 1.e+0
#mu_list = [-989.926, -428.451] # ag, o - p/p0 = 1.e-10
#mu_list = [-989.926, -428.333] # ag, o - p/p0 = 1.e-6
#mu_list = [-989.926, -428.215] # ag, o - p/p0 = 1.e-2
mu_list = [-989.926, -428.096] # ag, o - p/p0 = 1.e+2
act_p = np.array([1e-5, 0, 0, 1, 1]) # probablity of taking different actions
# [0]: move, [1]: swap, [2]: jump, [3]: add, [4]: remove
fail_en = 999.
nproc = 144
nkdiv = 1
ndiag = 144
###########################
# GET ELEMENT INFORMATION #
###########################
el = el_info()
el.pop_attr(el_filename,T_move)
##############################
# GET STRUCTURAL INFORMATION #
##############################
xsf = xsf_info()
xsf.pop_attr(xsf_filename, el, buf_len)
####################################
# GET NEAREST NEIGHBOR INFORMATION #
####################################
bvo = bv()
bvo.init(xsf, el)
###################################
# INSTANTIATE MONTE CARLO ROUTINE #
###################################
mc_run = mc()
mc_run.init(T_move, max_disp, xsf)
###################################
# RUN GRAND CANONICAL MONTE CARLO #
###################################
os.system('mkdir -p temp') # make temp directory for qe calculations
os.chdir('temp') # enter temp
log_file = init_log('log.dat') # initialize log file
axsf_opt_file = init_axsf('coord_opt.axsf', niter, xsf) # " " axsf file recording optimized structure
axsf_new_file = init_axsf('coord_new.axsf', niter, xsf) # " " structure created in current iteration
axsf_accept_file = init_axsf('coord_accept.axsf', niter, xsf) # " " accepted in current iteration
axsf_failed_file = init_axsf('coord_failed.axsf', niter, xsf) # initialize axsf file recording structure failed in qe
axsf_failed_iter_file = init_axsf('coord_failed_iter.axsf', niter, xsf) # initialize axsf file recording structure failed in qe
failed_cnt = 0
for i in range(niter) :
xsf.get_r_min_max(buf_len)
xsf.get_vol_np()
# attempt uvt action and store xsf attributes in xsf_new
if i == 0 :
# alway start with move
xsf = mc_run.uvt_new_structure_np(xsf, el, np.array([1,0,0,0,0]), bvo)
else :
xsf = mc_run.uvt_new_structure_np(xsf, el, act_p, bvo)
# make input file
make_qe_in('qe.in', xsf, el)
# if not move step, then replace scf with relax
if mc_run.uvt_act != 0 :
os.system('sed -i "s/scf/relax/g" qe.in')
# if number of atoms is smaller than or equal to 6, make sure it has 36 bands
# note that in the future it should be evaluated based on number of electrons
# and the threshold for the number of bands should be -ndiag in qe
if xsf.at_num <= 6 :
os.system('sed -i "/&SYSTEM/a nbnd = 36" qe.in')
# calculate and get total energy
call_qe = 'mpiexec_mpt -np ' + str(nproc) + ' ../bin/pw.x -nk ' + str(nkdiv) + ' -ndiag ' + str(ndiag) + ' -i qe.in > qe.out'
subprocess.call(call_qe, shell = True)
qe_out = qe_out_info('qe.out')
# get energy and forces from qe
if os.popen('grep ! qe.out').read() == '' :
# qe failed at first scf step
new_en = fail_en
mc_run.new_xsf.at_force = np.zeros((mc_run.new_xsf.at_num, 3))
else :
# get energy from qe
new_en = qe_out.get_final_en()
# get forces from qe
mc_run.new_xsf.at_force = qe_out.get_forces(mc_run.new_xsf.at_num) * ry_ev / bohr_ang # convert forces from ry/bohr to ev/ang
# if not move step and qe does not fail at first step, update atomic coordinates
if ( os.popen('grep ! qe.out').read() != '' and mc_run.uvt_act != 0 ) :
mc_run.new_xsf.at_coord = qe_out.get_coord(mc_run.new_xsf.at_num)
# update T
mc_run.update_T_const(i - failed_cnt, 3000)
# decide whether or not to accept uvt action
# note that old_xsf is changed to new_xsf if accepted
accept = mc_run.uvt_mc(new_en, el, mu_list)
# calculate free energy
free_en, _ = mc_run.get_free_g_p(new_en, el, mu_list)
# if step not accepted, copy attributes from old (previous) xsf to xsf
if accept == 0 :
xsf = mc_run.old_xsf.copy()
# othewise copy new xsf to xsf
else :
xsf = mc_run.new_xsf.copy()
# update logs if no qe error
if new_en != fail_en :
# write energies, number of accepted steps, and acceptance rate to log file
upd_log(log_file, i - failed_cnt, free_en, mc_run)
# write atomic coordinates to axsf file
upd_axsf(axsf_opt_file, i - failed_cnt, mc_run.opt_xsf, el)
upd_axsf(axsf_new_file, i - failed_cnt, mc_run.new_xsf, el)
upd_axsf(axsf_accept_file, i - failed_cnt, mc_run.old_xsf, el)
else :
failed_cnt += 1
upd_axsf(axsf_failed_file, failed_cnt, mc_run.new_xsf, el)
upd_axsf(axsf_failed_iter_file, i, mc_run.new_xsf, el)
log_file.close()
axsf_opt_file.close()
axsf_new_file.close()
axsf_accept_file.close()
axsf_failed_file.close()
axsf_failed_iter_file.close()
os.chdir('../')