Skip to content

Commit 1ca3d6a

Browse files
committed
Added a new module to compute the electron-phonon coupling
1 parent 8957d82 commit 1ca3d6a

1 file changed

Lines changed: 270 additions & 0 deletions

File tree

Modules/AdvancedCalculations.py

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
import cellconstructor as CC
2+
import sscha, sscha.Cluster
3+
import threading, copy
4+
5+
import numpy as np
6+
7+
import sys, os
8+
9+
MODULE_DESCRIPTION = '''
10+
This module contains useful functions to do post-processing analysis of the SSCHA.
11+
12+
For example, it implements custom cluster calculators to compute the electron-phonon effect on
13+
absorbption and the bandgap.
14+
15+
'''
16+
17+
18+
class OpticalQECluster(sscha.Cluster.Cluster):
19+
'''
20+
This class does the same as the sscha.Cluster to submit the calculation of an ensemble
21+
but it also computes the spectral properties.
22+
'''
23+
24+
def __init__(self, new_k_grid = None, random_offset = True, epsilon_data = None,
25+
epsilon_binary = 'epsilon.x -npool NPOOL -i PREFIX.pwi > PREFIX.pwo',
26+
**kwargs):
27+
'''
28+
Initialize the cluster object.
29+
30+
Parameters
31+
----------
32+
33+
new_k_grid : list
34+
The dimension of the new k mesh to run the nscf calculation
35+
random_offset : bool
36+
If True, a random offset is added to the calculation
37+
epsilon_data : dict
38+
The dictionary with the information of the namespace for the
39+
epsilon.x file
40+
epsilon_binary : string
41+
The path to the epsilon.x binary inside the cluster.
42+
**kwargs :
43+
All other arguments to be passed to the cluster.
44+
45+
'''
46+
self.new_k_grid = new_k_grid
47+
48+
if random_offset:
49+
self.random_offset = np.random.uniform(size = 3)
50+
else:
51+
self.random_offset = np.zeros(3)
52+
53+
self.kpts = None
54+
self.epsilon_binary = epsilon_binary
55+
56+
57+
self.read_sigma = False
58+
59+
self.epsilon_data = {
60+
'inputpp' : {
61+
'calculation' : 'sigma',
62+
'prefix' : 'AHAH'
63+
},
64+
'energy_grid' : {
65+
'wmin' : 0,
66+
'wmax' : 40,
67+
'nw' : 10000,
68+
'temperature' : 300
69+
}
70+
}
71+
if epsilon_data is not None:
72+
self.epsilon_data = epsilon_data
73+
74+
super().__init__(**kwargs)
75+
76+
77+
def __setattr__(self, __name, __value):
78+
super().__setattr__(__name, __value)
79+
80+
# Always regenerate the kpts
81+
if __name == 'new_k_grid' and not __value is None:
82+
assert len(__value) == 3, 'Error, new_k_grid must be a tuple with 3 elements'
83+
self.generate_kpts()
84+
85+
86+
def generate_kpts(self):
87+
'''
88+
Generate the kpts for the non self-consistent calculation
89+
'''
90+
91+
nkpts = int(np.prod(self.new_k_grid))
92+
93+
self.kpts = np.zeros((nkpts, 3), dtype = np.double)
94+
95+
for i in range(nkpts):
96+
x = float(i // (self.new_k_grid[1] * self.new_k_grid[2]))
97+
res = i % (self.new_k_grid[1] * self.new_k_grid[2])
98+
y = float(res // self.new_k_grid[2])
99+
z = float(res % self.new_k_grid[2])
100+
101+
x /= self.new_k_grid[0]
102+
y /= self.new_k_grid[1]
103+
z /= self.new_k_grid[2]
104+
105+
106+
x = (x + self.random_offset[0]) % 1
107+
y = (y + self.random_offset[1]) % 1
108+
z = (z + self.random_offset[2]) % 1
109+
110+
self.kpts[i, :] = [x, y, z]
111+
112+
113+
def get_execution_command(self, label):
114+
'''
115+
Get the command to execute the two pw.x and epsilon.x
116+
calculations
117+
'''
118+
119+
# Get the MPI command replacing NPROC
120+
new_mpicmd = self.mpi_cmd.replace("NPROC", str(self.n_cpu))
121+
122+
# Replace the NPOOL variable and the PREFIX in the binary
123+
binary = self.binary.replace("NPOOL", str(self.n_pool)).replace("PREFIX", label)
124+
binary_nscf = self.binary.replace("NPOOL", str(self.n_pool)).replace("PREFIX", label + '_nscf')
125+
binary_eps = self.epsilon_binary.replace("NPOOL", str(self.n_pool)).replace("PREFIX", label + '_eps')
126+
127+
tmt_str = ""
128+
if self.use_timeout:
129+
tmt_str = "timeout %d " % self.timeout
130+
131+
submission_txt = '''
132+
{0} {1} {2}
133+
{0} {1} {3}
134+
{0} {1} {4}
135+
'''.format(tmt_str, new_mpicmd, binary, binary_nscf, binary_eps)
136+
137+
# Remove the wavefunction and other heavy data
138+
submission_txt += '''
139+
rm -rf {0}.wfc* {0}.save
140+
141+
'''.format(label)
142+
143+
return submission_txt
144+
145+
146+
def read_results(self, calc, label):
147+
'''
148+
Get the results
149+
'''
150+
151+
results = super().read_results(calc, label)
152+
153+
print('THREAD ID {} START READING THE EPSILON'.format(threading.get_native_id()))
154+
155+
# Add the additional information related to the epsilon
156+
prefix = label
157+
epsilon_data = None
158+
eps_real = np.loadtxt(os.path.join(self.local_workdir, 'epsr_{}.dat'.format(prefix)))
159+
eps_imag = np.loadtxt(os.path.join(self.local_workdir, 'epsi_{}.dat'.format(prefix)))
160+
161+
nw = eps_real.shape[0]
162+
epsilon_data = np.zeros((nw, 3), dtype = np.double)
163+
164+
epsilon_data[:,0] = eps_real[:,0]
165+
epsilon_data[:, 1] = np.mean(eps_real[:, 1:], axis = 1)
166+
epsilon_data[:, 2] = np.mean(eps_imag[:, 2:], axis = 1)
167+
168+
results['epsilon'] = epsilon_data
169+
print('THREAD ID {} READED ALSO THE EPSILON!'.format(threading.get_native_id()))
170+
171+
return results
172+
173+
174+
175+
def prepare_input_file(self, structures, calc, labels):
176+
'''
177+
Prepare the input files for the cluster
178+
'''
179+
180+
181+
# Prepare the input file
182+
list_of_inputs = []
183+
list_of_outputs = []
184+
for i, (label, structure) in enumerate(zip(labels, structures)):
185+
# Avoid thread conflict
186+
self.lock.acquire()
187+
188+
try:
189+
calc.set_directory(self.local_workdir)
190+
PREFIX = label
191+
old_input = copy.deepcopy(calc.input_data)
192+
old_kpts = copy.deepcopy(calc.kpts)
193+
194+
195+
calc.input_data['control'].update({'prefix' : PREFIX})
196+
calc.set_label(label)
197+
calc.write_input(structure)
198+
199+
print("[THREAD {}] LBL: {} | PREFIX: {}".format(threading.get_native_id(), label, calc.input_data["control"]["prefix"]))
200+
201+
202+
# prepare the first scf calculation
203+
input_file = '{}.pwi'.format(label)
204+
output_file = '{}.pwo'.format(label)
205+
206+
list_of_inputs.append(input_file)
207+
list_of_outputs.append(output_file)
208+
209+
# prepare the nscf calculation
210+
calc.input_data['control'].update({'calculation' : 'nscf'})
211+
calc.input_data['system'].update({'nosym' : True})
212+
calc.kpts = self.kpts.copy()
213+
214+
# Generate the input file
215+
new_label = '{}_nscf'.format(label)
216+
calc.set_label(new_label, override_prefix = False)
217+
calc.write_input(structure)
218+
input_file = '{}.pwi'.format(new_label)
219+
output_file = '{}.pwo'.format(new_label)
220+
list_of_inputs.append(input_file)
221+
list_of_outputs.append(output_file)
222+
223+
224+
# Prepare the epsilon calculation
225+
eps_namelist = copy.deepcopy(self.epsilon_data)
226+
eps_namelist['inputpp'].update({'prefix' : calc.input_data['control']['prefix']})
227+
eps_lines = CC.Methods.write_namelist(eps_namelist)
228+
229+
230+
new_label = '{}_eps'.format(label)
231+
input_file = '{}.pwi'.format(new_label)
232+
output_file = '{}.pwo'.format(new_label)
233+
234+
# Write the epsilon input file
235+
eps_in_filename = os.path.join(self.local_workdir, input_file)
236+
with open(eps_in_filename, 'w') as fp:
237+
fp.writelines(eps_lines)
238+
print('fname: {} prepared'.format(eps_in_filename))
239+
240+
241+
242+
list_of_inputs.append(input_file)
243+
list_of_outputs.append(output_file)
244+
245+
# Append also the imaginary and real part of epsilon and sigma
246+
outputs = ['epsi_{}.dat', 'epsr_{}.dat', 'sigmai_{}.dat', 'sigmar_{}.dat']
247+
list_of_outputs += [x.format(PREFIX) for x in outputs]
248+
249+
250+
# Reset the calculator with the old data
251+
calc.input_data = old_input
252+
calc.kpts = old_kpts
253+
254+
255+
256+
except Exception as e:
257+
MSG = '''
258+
Error while writing input file {}.
259+
'''.format(label)
260+
print(MSG)
261+
print('ERROR MSG:')
262+
print(e)
263+
264+
# Release the lock on the threads
265+
self.lock.release()
266+
267+
print('THREAD: {} inputs: {} outputs: {}'.format(threading.get_native_id(), list_of_inputs, list_of_outputs))
268+
269+
270+
return list_of_inputs, list_of_outputs

0 commit comments

Comments
 (0)