@@ -172,7 +172,17 @@ def convert_to_pyscal(self):
172172 """
173173 Convert a given system to pyscal and give a dict of type mappings
174174 """
175- aseobj = read (self .calc .lattice , format = "lammps-data" , style = "atomic" )
175+ # Create Z_of_type mapping to properly read LAMMPS data files
176+ # This ensures atoms are correctly identified by their element
177+ Z_of_type = dict (
178+ [
179+ (count + 1 , element (el ).atomic_number )
180+ for count , el in enumerate (self .calc .element )
181+ ]
182+ )
183+ aseobj = read (
184+ self .calc .lattice , format = "lammps-data" , style = "atomic" , Z_of_type = Z_of_type
185+ )
176186 pstruct = pc .System (aseobj , format = "ase" )
177187
178188 # here we have to validate the input composition dict; and map it
@@ -191,7 +201,6 @@ def convert_to_pyscal(self):
191201 self .actual_species = len (self .typedict )
192202 self .new_species = len (self .output_chemical_composition ) - len (self .typedict )
193203 self .maxtype = self .actual_species + 1 # + self.new_species
194- # print(self.typedict)
195204
196205 def get_composition_transformation (self ):
197206 """
@@ -215,26 +224,28 @@ def get_composition_transformation(self):
215224 self .to_remove = to_remove
216225 self .to_add = to_add
217226
218- def get_random_index_of_species (self , species ):
227+ def get_random_index_of_species (self , species_name ):
219228 """
220- Get a random index of a given species
229+ Get a random index of a given species by element name
221230 """
222- ids = [count for count , x in enumerate (self .atom_type ) if x == species ]
231+ ids = [count for count , x in enumerate (self .atom_species ) if x == species_name ]
223232 return ids [np .random .randint (0 , len (ids ))]
224233
225234 def mark_atoms (self ):
226235 for i in range (self .natoms ):
227236 self .atom_mark .append (False )
228237
238+ # Use species (element symbols) instead of numeric types
239+ self .atom_species = self .pyscal_structure .atoms .species
229240 self .atom_type = self .pyscal_structure .atoms .types
230- self .mappings = [f"{ x } -{ x } " for x in self .atom_type ]
241+ self .mappings = [f"{ x } -{ x } " for x in self .atom_species ]
231242
232243 def update_mark_atoms (self ):
233244 self .marked_atoms = []
234245 for key , val in self .to_remove .items ():
235- # print(f"Element { key}, count {val} ")
246+ # key is the element name (e.g., "Mg ")
236247 for i in range (100000 ):
237- rint = self .get_random_index_of_species (self . typedict [ key ] )
248+ rint = self .get_random_index_of_species (key )
238249 if rint not in self .marked_atoms :
239250 self .atom_mark [rint ] = True
240251 self .marked_atoms .append (rint )
@@ -257,22 +268,20 @@ def update_typedicts(self):
257268
258269 def compute_possible_mappings (self ):
259270 self .possible_mappings = []
260- # Now make a list of possible mappings
271+ # Now make a list of possible mappings using element names
261272 for key1 , val1 in self .to_remove .items ():
262273 for key2 , val2 in self .to_add .items ():
263274 mapping = f"{ key1 } -{ key2 } "
264275 if mapping not in self .restrictions :
265- self .possible_mappings .append (
266- f"{ self .typedict [key1 ]} -{ self .typedict [key2 ]} "
267- )
276+ self .possible_mappings .append (mapping )
268277
269278 def update_mappings (self ):
270279 marked_atoms = self .marked_atoms .copy ()
271280 for key , val in self .to_add .items ():
272281 # now get all
273282
274283 # we to see if we can get val number of atoms from marked ones
275- if val < len (marked_atoms ):
284+ if val > len (marked_atoms ):
276285 raise ValueError (
277286 f"Not enough atoms to choose { val } from { len (marked_atoms )} not possible"
278287 )
@@ -284,8 +293,8 @@ def update_mappings(self):
284293 to_del = []
285294 for x in range (len (self .marked_atoms )):
286295 random_choice = np .random .choice (marked_atoms )
287- # find corresponding mappiong
288- mapping = f"{ self .atom_type [random_choice ]} -{ self . typedict [ key ] } "
296+ # find corresponding mapping using species name
297+ mapping = f"{ self .atom_species [random_choice ]} -{ key } "
289298 if mapping in self .possible_mappings :
290299 # this is a valid choice
291300 self .mappings [random_choice ] = mapping
@@ -314,12 +323,8 @@ def update_mappings(self):
314323 mapsplit = mapping .split ("-" )
315324 if not mapsplit [0 ] == mapsplit [1 ]:
316325 transformation_dict = {}
317- transformation_dict ["primary_element" ] = self .reversetypedict [
318- int (mapsplit [0 ])
319- ]
320- transformation_dict ["secondary_element" ] = self .reversetypedict [
321- int (mapsplit [1 ])
322- ]
326+ transformation_dict ["primary_element" ] = mapsplit [0 ]
327+ transformation_dict ["secondary_element" ] = mapsplit [1 ]
323328 transformation_dict ["count" ] = self .unique_mapping_counts [count ]
324329 self .transformation_list .append (transformation_dict )
325330
@@ -333,25 +338,34 @@ def prepare_pair_lists(self):
333338 self .pair_list_new = []
334339 for mapping in self .unique_mappings :
335340 map_split = mapping .split ("-" )
336- # conserved atom
341+ # conserved atom - mappings now use element names directly
337342 if map_split [0 ] == map_split [1 ]:
338- self .pair_list_old .append (self . reversetypedict [ int ( map_split [0 ]) ])
339- self .pair_list_new .append (self . reversetypedict [ int ( map_split [0 ]) ])
343+ self .pair_list_old .append (map_split [0 ])
344+ self .pair_list_new .append (map_split [0 ])
340345 else :
341- self .pair_list_old .append (self .reversetypedict [int (map_split [0 ])])
342- self .pair_list_new .append (self .reversetypedict [int (map_split [1 ])])
346+ self .pair_list_old .append (map_split [0 ])
347+ self .pair_list_new .append (map_split [1 ])
348+
349+ # Special case: 100% transformation with only 1 mapping
350+ # LAMMPS expects elements for all atom types in the system
351+ # Example: Al→Mg only, but system has 2 types → need ['Al', 'Al'] and ['Mg', 'Mg']
352+ n_elements = len (self .calc .element )
353+ if len (self .unique_mappings ) == 1 and n_elements > 1 :
354+ # Duplicate the single mapping to match number of element types
355+ for _ in range (n_elements - 1 ):
356+ self .pair_list_old .append (self .pair_list_old [0 ])
357+ self .pair_list_new .append (self .pair_list_new [0 ])
358+
343359 self .new_atomtype = np .array (range (len (self .unique_mappings ))) + 1
344360 self .mappingdict = dict (zip (self .unique_mappings , self .new_atomtype ))
345361
346362 def update_types (self ):
363+ # Update atom_type based on mapping to new types
347364 for x in range (len (self .atom_type )):
348365 self .atom_type [x ] = self .mappingdict [self .mappings [x ]]
349366
350- # smartify these loops
351- # npyscal = len(self.pyscal_structure.atoms.types)
367+ # Update pyscal structure types
352368 self .pyscal_structure .atoms .types = self .atom_type
353- # for count in range(npyscal)):
354- # self.pyscal_structure.atoms.types[count] = self.atom_type[count]
355369
356370 def iselement (self , symbol ):
357371 try :
@@ -392,7 +406,7 @@ def update_pair_coeff(self, pair_coeff):
392406 # If element_group matches our pair_list_old, replace with pair_list_new
393407 # Otherwise replace with pair_list_old (for the old/reference command)
394408 if element_group == self .pair_list_old or set (element_group ) == set (
395- self .element
409+ self .calc . element
396410 ):
397411 # This needs special handling - we'll mark position for later
398412 result_parts .append ("__ELEMENTS__" )
@@ -432,12 +446,15 @@ def update_pair_coeff(self, pair_coeff):
432446
433447 def get_swap_types (self ):
434448 """
435- Get swapping types
449+ Get swapping types for configurational entropy calculation.
450+
451+ Returns types that share the same initial element but have different
452+ transformation paths (e.g., Al→Al vs Al→Mg).
436453 """
437454 swap_list = []
438455 for mapping in self .unique_mappings :
439456 map_split = mapping .split ("-" )
440- # conserved atom
457+ # conserved atom - skip
441458 if map_split [0 ] == map_split [1 ]:
442459 pass
443460 else :
@@ -446,12 +463,19 @@ def get_swap_types(self):
446463 first_map = f"{ first_type } -{ first_type } "
447464 second_map = mapping
448465
449- # get the numbers from dict
450- first_swap_type = self .mappingdict [first_map ]
451- second_swap_type = self .mappingdict [second_map ]
466+ # Check if conserved mapping exists
467+ if first_map in self .mappingdict :
468+ # get the numbers from dict
469+ first_swap_type = self .mappingdict [first_map ]
470+ second_swap_type = self .mappingdict [second_map ]
471+ swap_list .append ([first_swap_type , second_swap_type ])
472+ else :
473+ # 100% transformation case - no conserved atoms of this type
474+ # Only the transforming type exists
475+ second_swap_type = self .mappingdict [second_map ]
476+ swap_list .append ([second_swap_type ])
452477
453- swap_list .append ([first_swap_type , second_swap_type ])
454- return swap_list [0 ]
478+ return swap_list [0 ] if swap_list else []
455479
456480 def write_structure (self , outfilename ):
457481 # create some species dict
0 commit comments