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