@@ -93,17 +93,65 @@ def collect_force(abacusjob_dir):
9393
9494 return metrics ['force' ]
9595
96- def filter_force (forces , selected_atoms ):
96+ def prepare_abacus_vibration_analysis (input_params : Dict [str , Any ],
97+ stru : AbacusStru ,
98+ work_path : Path ,
99+ abacus_inputs_dir : Path ,
100+ selected_atoms : Optional [List [int ]] = None ,
101+ stepsize : float = 0.01 ,
102+ vib_cache_dir : str = "vib" ):
97103 """
98- Select forces belong to selected atoms
104+ Prepare ABACUS input files for vibrational analysis.
99105 """
100- selected_atoms_force_idx = []
101- for selected_atom in selected_atoms :
102- selected_atoms_force_idx += [selected_atom * 3 , selected_atom * 3 + 1 , selected_atom * 3 + 2 ]
106+ stru_file = input_params .get ('stru_file' , "STRU" )
107+ displaced_stru = copy .deepcopy (stru )
108+ original_stru_coord = np .array (stru .get_coord (bohr = False , direct = False ))
109+
110+ if selected_atoms is None :
111+ selected_atoms = [i for i in range (stru .get_natoms ())]
112+
113+ selected_atoms .sort ()
103114
104- filtered_force = np .array (forces )[selected_atoms_force_idx ]
115+ DIRECTION_MAP = ['x' , 'y' , 'z' ]
116+ STEP_MAP = {'+' : 1 , '-' : - 1 }
117+ disped_stru_job_paths = []
118+ disped_stru_cache_labels = []
119+
120+ # Prepare ABACUS input files for the given structure
121+ abacus_scf_work_path = os .path .join (work_path , "SCF" )
122+ original_stru_job_path = os .path .join (abacus_scf_work_path , "eq" )
123+ os .makedirs (original_stru_job_path )
105124
106- return filtered_force
125+ link_abacusjob (src = abacus_inputs_dir ,
126+ dst = original_stru_job_path ,
127+ copy_files = ['INPUT' , 'STRU' , 'KPT' , 'abacus.log' ],
128+ exclude_directories = True )
129+ WriteInput (input_params , os .path .join (original_stru_job_path , "INPUT" ))
130+ disped_stru_job_paths .append (original_stru_job_path )
131+ disped_stru_cache_labels .append (os .path .join (vib_cache_dir , 'cache.eq.json' ))
132+
133+ # Prepare ABACUS input files for each displaced structure. nfree is assumed to be 2.
134+ for selected_atom in selected_atoms :
135+ for direction in range (3 ): # x, y and z directions
136+ displaced_stru_coord = copy .deepcopy (original_stru_coord )
137+ for step in STEP_MAP .keys (): # Two steps along one direction
138+ disped_stru_job_path = os .path .join (abacus_scf_work_path , f"disp-{ selected_atom } -{ DIRECTION_MAP [direction ]} { step } " )
139+ os .makedirs (disped_stru_job_path )
140+
141+ link_abacusjob (src = abacus_inputs_dir ,
142+ dst = disped_stru_job_path ,
143+ copy_files = ['INPUT' , 'STRU' , 'KPT' , 'abacus.log' ],
144+ exclude_directories = True )
145+
146+ displaced_stru_coord [selected_atom ][direction ] = original_stru_coord [selected_atom ][direction ] + stepsize * STEP_MAP [step ]
147+ displaced_stru .set_coord (displaced_stru_coord , bohr = False , direct = False )
148+ WriteInput (input_params , os .path .join (disped_stru_job_path , "INPUT" ))
149+ displaced_stru .write (os .path .join (disped_stru_job_path , stru_file ))
150+
151+ disped_stru_job_paths .append (disped_stru_job_path )
152+ disped_stru_cache_labels .append (os .path .join (vib_cache_dir , f"cache.{ selected_atom } { DIRECTION_MAP [direction ]} { step } .json" ))
153+
154+ return disped_stru_job_paths , disped_stru_cache_labels
107155
108156def abacus_vibration_analysis (abacus_inputs_dir : Path ,
109157 selected_atoms : Optional [List [int ]] = None ,
@@ -142,7 +190,7 @@ def abacus_vibration_analysis(abacus_inputs_dir: Path,
142190 raise ValueError ("stepsize should be positive." )
143191
144192 work_path = Path (generate_work_path ()).absolute ()
145-
193+
146194 input_params = ReadInput (os .path .join (abacus_inputs_dir , "INPUT" ))
147195 stru_file = input_params .get ('stru_file' , "STRU" )
148196 stru = AbacusStru .ReadStru (os .path .join (abacus_inputs_dir , stru_file ))
@@ -157,66 +205,26 @@ def abacus_vibration_analysis(abacus_inputs_dir: Path,
157205 indices = selected_atoms ,
158206 delta = stepsize ,
159207 nfree = nfree )
160-
161- displaced_stru = copy .deepcopy (stru )
162- original_stru_coord = np .array (stru .get_coord (bohr = False , direct = False ))
163-
164- if selected_atoms is None :
165- selected_atoms = [i for i in range (stru .get_natoms ())]
166-
167- selected_atoms .sort ()
168-
169- DIRECTION_MAP = ['x' , 'y' , 'z' ]
170- STEP_MAP = {'+' : 1 , '-' : - 1 }
171- disped_stru_job_paths = []
172- disped_stru_cache_labels = []
173-
174- # Prepare ABACUS input files for the given structure
175- abacus_scf_work_path = os .path .join (work_path , "SCF" )
176- original_stru_job_path = os .path .join (abacus_scf_work_path , "eq" )
177- os .makedirs (original_stru_job_path )
178-
179- link_abacusjob (src = abacus_inputs_dir ,
180- dst = original_stru_job_path ,
181- copy_files = ['INPUT' , 'STRU' , 'KPT' , 'abacus.log' ],
182- exclude_directories = True )
183- WriteInput (input_params , os .path .join (original_stru_job_path , "INPUT" ))
184- disped_stru_job_paths .append (original_stru_job_path )
185- disped_stru_cache_labels .append (os .path .join (vib_cache_dir , 'cache.eq.json' ))
186-
187- # Prepare ABACUS input files for each displaced structure. nfree is assumed to be 2.
188- for selected_atom in selected_atoms :
189- for direction in range (3 ): # x, y and z directions
190- displaced_stru_coord = copy .deepcopy (original_stru_coord )
191- for step in STEP_MAP .keys (): # Two steps along one direction
192- disped_stru_job_path = os .path .join (abacus_scf_work_path , f"disp-{ selected_atom } -{ DIRECTION_MAP [direction ]} { step } " )
193- os .makedirs (disped_stru_job_path )
194-
195- link_abacusjob (src = abacus_inputs_dir ,
196- dst = disped_stru_job_path ,
197- copy_files = ['INPUT' , 'STRU' , 'KPT' , 'abacus.log' ],
198- exclude_directories = True )
199-
200- displaced_stru_coord [selected_atom ][direction ] = original_stru_coord [selected_atom ][direction ] + stepsize * STEP_MAP [step ]
201- displaced_stru .set_coord (displaced_stru_coord , bohr = False , direct = False )
202- WriteInput (input_params , os .path .join (disped_stru_job_path , "INPUT" ))
203- displaced_stru .write (os .path .join (disped_stru_job_path , stru_file ))
204208
205- disped_stru_job_paths .append (disped_stru_job_path )
206- disped_stru_cache_labels .append (os .path .join (vib_cache_dir , f"cache.{ selected_atom } { DIRECTION_MAP [direction ]} { step } .json" ))
209+ disped_stru_job_paths , disped_stru_cache_labels = prepare_abacus_vibration_analysis (input_params ,
210+ stru ,
211+ work_path = work_path ,
212+ abacus_inputs_dir = abacus_inputs_dir ,
213+ selected_atoms = selected_atoms ,
214+ stepsize = stepsize ,
215+ vib_cache_dir = vib_cache_dir )
207216
208217 # Run ABACUS calculation for all prepared jobs
209218 run_abacus (disped_stru_job_paths )
210219
211220 # Collect needed forces for each ABACUS calculation and dump to cache directory used by ase.vibrations
212221 cache_forces_json = {"forces" : {
213- "__ndarray__" : [[len ( selected_atoms ), 3 ],
222+ "__ndarray__" : [[stru . get_natoms ( ), 3 ],
214223 "float64" ,
215224 []]
216225 }}
217226 for disped_stru_job_path , disped_stru_cache_label in list (zip (disped_stru_job_paths , disped_stru_cache_labels )):
218- force = collect_force (disped_stru_job_path )
219- cache_forces_json ["forces" ]["__ndarray__" ][2 ] = list (filter_force (force , selected_atoms ))
227+ cache_forces_json ["forces" ]["__ndarray__" ][2 ] = collect_force (disped_stru_job_path )
220228 with open (os .path .join (vib_cache_dir , disped_stru_cache_label ), "w" ) as fin :
221229 json .dump (cache_forces_json , fin )
222230
@@ -253,6 +261,5 @@ def abacus_vibration_analysis(abacus_inputs_dir: Path,
253261 'vib_free_energy' : float (free_energy ),
254262 'vib_analysis_work_dir' : Path (work_path ).absolute ()}
255263 except Exception as e :
256- print (e )
257264 return {'message' : f"Doing vibration analysis failed: { e } " }
258265
0 commit comments