99from monty .serialization import dumpfn , loadfn
1010from pymatgen .analysis .defects .generators import VoronoiInterstitialGenerator
1111from pymatgen .analysis .defects .core import Interstitial as pmgInterstitial
12- from pymatgen .core .structure import Structure
12+ from pymatgen .core .structure import Structure , Lattice
1313from pymatgen .core .sites import PeriodicSite
14+ from pymatgen .core .tensors import Tensor
15+ from pymatgen .core .operations import SymmOp
1416
1517from apex .core .calculator .lib import abacus_utils
1618from apex .core .calculator .lib import lammps_utils
2325upload_packages .append (__file__ )
2426
2527PREDEFINED_LIST = ['bcc' , 'fcc' , 'hcp' ]
28+ TOL = 1e-5
2629
2730class Interstitial (Property ):
2831 def __init__ (self , parameter , inter_param = None ):
@@ -33,7 +36,7 @@ def __init__(self, parameter, inter_param=None):
3336 default_supercell = [1 , 1 , 1 ]
3437 parameter ["supercell" ] = parameter .get ("supercell" , default_supercell )
3538 self .supercell = parameter ["supercell" ]
36- self .insert_ele = parameter [ "insert_ele" ]
39+ self .insert_ele = parameter . get ( "insert_ele" , None )
3740 parameter ["lattice_type" ] = parameter .get ("lattice_type" , None )
3841 self .lattice_type = parameter ["lattice_type" ]
3942 parameter ["voronoi_param" ] = parameter .get ("voronoi_param" , {})
@@ -64,7 +67,7 @@ def __init__(self, parameter, inter_param=None):
6467 def make_confs (self , path_to_work , path_to_equi , refine = False ):
6568 self .path_to_work = os .path .abspath (path_to_work )
6669 if os .path .exists (path_to_work ):
67- logging .warning ("%s already exists" % path_to_work )
70+ logging .debug ("%s already exists" % path_to_work )
6871 else :
6972 os .makedirs (path_to_work )
7073 path_to_equi = os .path .abspath (path_to_equi )
@@ -157,7 +160,9 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
157160 ss = abacus_utils .stru2Structure (equi_contcar )
158161 else :
159162 ss = Structure .from_file (equi_contcar )
160-
163+ rot = Tensor .get_ieee_rotation (ss )
164+ op = SymmOp .from_rotation_and_translation (rot )
165+ ss .apply_operation (op )
161166 # get structure type
162167 os .chdir (self .path_to_work )
163168 # convert site element into same type for a pseudo-structure just for simple lattice type judgment
@@ -171,6 +176,7 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
171176 orig_st = StructureInfo (ss , symprec = 0.1 , angle_tolerance = 5 )
172177 conv_ss = orig_st .conventional_structure
173178 conv_ss .to ("POSCAR" , "POSCAR" )
179+ conv_ss .to ("POSCAR_conv" , "POSCAR" )
174180 ss = conv_ss
175181 if self .lattice_type :
176182 print (f'Adopt user indicated lattice type: { self .lattice_type } ' )
@@ -182,8 +188,31 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
182188 self .insert_element_task = os .path .join (self .path_to_work , "element.out" )
183189 if os .path .isfile (self .insert_element_task ):
184190 os .remove (self .insert_element_task )
191+ if not self .insert_ele :
192+ self .insert_ele = [str (ii ) for ii in set (ss .composition .elements )]
185193 for ii in self .insert_ele :
186194 if self .structure_type in PREDEFINED_LIST :
195+ # rotate and translate hcp structure to specific orientation for interstitial generation
196+ if self .structure_type == 'hcp' :
197+ theta = - 2 * np .pi / 3
198+ rot_m = np .array ([
199+ [np .cos (theta ), - np .sin (theta ), 0 ],
200+ [np .sin (theta ), np .cos (theta ), 0 ],
201+ [0 , 0 , 1 ]
202+ ])
203+ op = SymmOp .from_rotation_and_translation (rotation_matrix = rot_m )
204+ ss .apply_operation (op )
205+ new_lattice = Lattice ([
206+ ss .lattice .matrix [0 ] * - 1 , ss .lattice .matrix [1 ] * - 1 , ss .lattice .matrix [2 ]
207+ ])
208+ new_frac_coords = ss .frac_coords .copy ()
209+ if not ((new_frac_coords [0 ][0 ] < 0.5 and new_frac_coords [0 ][2 ] < 0.5 )\
210+ or (new_frac_coords [0 ][0 ] > 0.5 and new_frac_coords [0 ][2 ] > 0.5 )):
211+ new_frac_coords [0 ][2 ] = ss .frac_coords [1 ][2 ]
212+ new_frac_coords [1 ][2 ] = ss .frac_coords [0 ][2 ]
213+ new_ss = Structure (new_lattice , ss .species , new_frac_coords , coords_are_cartesian = False )
214+ ss = new_ss
215+ ss .to (os .path .join (self .path_to_work , 'POSCAR_conv' ), 'POSCAR' )
187216 # produce a pseudo interstitial structure for later modification
188217 vds = [pmgInterstitial (ss , PeriodicSite (ii , [0.12 , 0.13 , 0.14 ], ss .lattice ))]
189218 else :
@@ -250,10 +279,11 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
250279
251280 # create pre-defined special SIA structure for bcc fcc and hcp
252281 if self .structure_type in PREDEFINED_LIST :
282+ self .task_list = []
253283 if not os .path .isfile ("task.000000/POSCAR" ):
254284 raise RuntimeError ("need task.000000 structure as reference" )
255285
256- with open ('POSCAR ' , "r" ) as fin :
286+ with open ('POSCAR_conv ' , "r" ) as fin :
257287 fin .readline ()
258288 scale = float (fin .readline ().split ()[0 ])
259289 self .latt_param = float (fin .readline ().split ()[0 ])
@@ -266,13 +296,12 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
266296 ss = ii .split ()
267297 if len (ss ) > 3 :
268298 if (
269- abs (0.12 / self .supercell [0 ] - float (ss [0 ])) < 1e-5
270- and abs (0.13 / self .supercell [1 ] - float (ss [1 ])) < 1e-5
271- and abs (0.14 / self .supercell [2 ] - float (ss [2 ])) < 1e-5
299+ abs (0.12 / self .supercell [0 ] - float (ss [0 ])) < TOL
300+ and abs (0.13 / self .supercell [1 ] - float (ss [1 ])) < TOL
301+ and abs (0.14 / self .supercell [2 ] - float (ss [2 ])) < TOL
272302 ):
273303 chl = idx
274- # pseudo-task only run original POSCAR to save calculation resources
275- shutil .copyfile ("POSCAR" , "task.000000/POSCAR" )
304+ shutil .rmtree ("task.000000" )
276305
277306 os .chdir (cwd )
278307 # specify interstitial structures
@@ -281,9 +310,9 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
281310 ss = ii .split ()
282311 if len (ss ) > 3 :
283312 if (
284- abs (0.5 / self .supercell [0 ] - float (ss [0 ])) < 1e-5
285- and abs (0.5 / self .supercell [1 ] - float (ss [1 ])) < 1e-5
286- and abs (0.5 / self .supercell [2 ] - float (ss [2 ])) < 1e-5
313+ abs (0.5 / self .supercell [0 ] - float (ss [0 ])) < TOL
314+ and abs (0.5 / self .supercell [1 ] - float (ss [1 ])) < TOL
315+ and abs (0.5 / self .supercell [2 ] - float (ss [2 ])) < TOL
287316 ):
288317 center = idx
289318 bcc_interstital_dict = {
@@ -304,16 +333,16 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
304333 ss = ii .split ()
305334 if len (ss ) > 3 :
306335 if (
307- abs (1 / self .supercell [0 ] - float (ss [0 ])) < 1e-5
308- and abs (0.5 / self .supercell [1 ] - float (ss [1 ])) < 1e-5
309- and abs (0.5 / self .supercell [2 ] - float (ss [2 ])) < 1e-5
336+ abs (1 / self .supercell [0 ] - float (ss [0 ])) < TOL
337+ and abs (0.5 / self .supercell [1 ] - float (ss [1 ])) < TOL
338+ and abs (0.5 / self .supercell [2 ] - float (ss [2 ])) < TOL
310339 ):
311340 face = idx
312341
313342 if (
314- abs (1 / self .supercell [0 ] - float (ss [0 ])) < 1e-5
315- and abs (1 / self .supercell [1 ] - float (ss [1 ])) < 1e-5
316- and abs (1 / self .supercell [2 ] - float (ss [2 ])) < 1e-5
343+ abs (1 / self .supercell [0 ] - float (ss [0 ])) < TOL
344+ and abs (1 / self .supercell [1 ] - float (ss [1 ])) < TOL
345+ and abs (1 / self .supercell [2 ] - float (ss [2 ])) < TOL
317346 ):
318347 corner = idx
319348
@@ -349,9 +378,9 @@ def make_confs(self, path_to_work, path_to_equi, refine=False):
349378 ss = ii .split ()
350379 if len (ss ) > 3 :
351380 if (
352- abs (1 / 3 / self .supercell [0 ] - float (ss [0 ])) < 1e-5
353- and abs (2 / 3 / self .supercell [1 ] - float (ss [1 ])) < 1e-5
354- and abs (0.25 / self .supercell [2 ] - float (ss [2 ])) < 1e-5
381+ abs (1 / 3 / self .supercell [0 ] - float (ss [0 ])) < TOL
382+ and abs (2 / 3 / self .supercell [1 ] - float (ss [1 ])) < TOL
383+ and abs (0.25 / self .supercell [2 ] - float (ss [2 ])) < TOL
355384 ):
356385 center = idx
357386 hcp_interstital_dict = {
@@ -387,7 +416,7 @@ def __gen_tasks(self, interstitial_dict):
387416 cwd = os .getcwd ()
388417 for ii , (type_str , adjust_dict ) in enumerate (interstitial_dict .items ()):
389418 output_task = os .path .join (
390- self .path_to_work , "task.%06d" % (len (self .dss ) + ii )
419+ self .path_to_work , "task.%06d" % (len (self .dss ) + ii - 1 )
391420 )
392421 os .makedirs (output_task , exist_ok = True )
393422 os .chdir (output_task )
@@ -414,10 +443,11 @@ def __gen_tasks(self, interstitial_dict):
414443 print (f"gen { type_str } " )
415444 os .chdir (cwd )
416445
417- total_task = len (self .dss ) + len (interstitial_dict )
446+ total_task = len (self .dss ) + len (interstitial_dict ) - 1
418447
419448 return total_task
420449
450+
421451 def post_process (self , task_list ):
422452 if True :
423453 fin1 = open (os .path .join (task_list [0 ], ".." , "element.out" ), "r" )
@@ -472,7 +502,7 @@ def _compute_lower(self, output_file, all_tasks, all_res):
472502 equi_result = loadfn (os .path .join (equi_path , "result.json" ))
473503 equi_epa = equi_result ["energies" ][- 1 ] / sum (equi_result ["atom_numbs" ])
474504
475- for idid , ii in enumerate (all_tasks [ 1 :] , start = 1 ): # skip task.000000
505+ for idid , ii in enumerate (all_tasks , start = 0 ): # skip task.000000
476506 structure_dir = os .path .basename (ii )
477507 task_result = loadfn (all_res [idid ])
478508 interstitial_type = loadfn (os .path .join (ii , 'interstitial_type.json' ))
0 commit comments