Skip to content
Snippets Groups Projects
Commit 8f9af5e0 authored by Saku Hakamaa's avatar Saku Hakamaa
Browse files

tag atoms and count their exchanges

parent ac59ec63
Branches
Tags
1 merge request!10Tag atoms and count their exchanges
...@@ -3,6 +3,7 @@ import numpy as np ...@@ -3,6 +3,7 @@ import numpy as np
import math import math
import ase.lattice.cubic import ase.lattice.cubic
import ase.data
import ase.io import ase.io
import ase.units import ase.units
...@@ -18,6 +19,7 @@ class AseSim: ...@@ -18,6 +19,7 @@ class AseSim:
self.atoms = None self.atoms = None
self.dyn = None self.dyn = None
self.run = None self.run = None
self.tagged_elements = {}
def create_lattice(self, lattice_size, basis, empty_space, pot_filename, rand_ox=False): def create_lattice(self, lattice_size, basis, empty_space, pot_filename, rand_ox=False):
self.atoms = ase.lattice.cubic.BodyCenteredCubic(basis, size=lattice_size, pbc=(True, True, False)) self.atoms = ase.lattice.cubic.BodyCenteredCubic(basis, size=lattice_size, pbc=(True, True, False))
...@@ -41,23 +43,22 @@ class AseSim: ...@@ -41,23 +43,22 @@ class AseSim:
nbor_list = NeighborList(nbor_cutoffs, skin=0.01, self_interaction=False, bothways=True) nbor_list = NeighborList(nbor_cutoffs, skin=0.01, self_interaction=False, bothways=True)
nbor_list.update(self.atoms) nbor_list.update(self.atoms)
pot_path = os.path.join('data', pot_filename)
self.dyn = SajuhakDynamics(self.atoms, nbor_list, pot_path, rand_ox)
self.run = self.dyn.run
self.atoms.set_calculator(KokkoPotential(nbor_list, pot_path))
# initialize atom modifications # initialize atom modifications
symbols = self.dyn.atoms.get_chemical_symbols() symbols = self.atoms.get_chemical_symbols()
positions = self.dyn.atoms.get_positions() positions = self.atoms.get_positions()
max_miller = lattice_size[2] * (1 - empty_space) max_miller = lattice_size[2] * (1 - empty_space)
# replace atoms with empty spaces above a certain z position # replace atoms with empty spaces above a certain z position
for i, position in enumerate(positions): for i, position in enumerate(positions):
if position[2] > self.dyn.atoms.millerbasis[2][2]*max_miller: if position[2] > self.atoms.millerbasis[2][2]*max_miller:
symbols[i] = 'X' symbols[i] = 'X'
self.dyn.atoms.set_chemical_symbols(symbols) # store modifications and initialize dyn and calc
pot_path = os.path.join('data', pot_filename)
self.atoms.set_chemical_symbols(symbols)
self.dyn = SajuhakDynamics(self.atoms, nbor_list, pot_path, rand_ox)
self.run = self.dyn.run
self.atoms.set_calculator(KokkoPotential(nbor_list, pot_path))
# @profile # add -m memory_profiler to interpreter options # @profile # add -m memory_profiler to interpreter options
def dope(self, dopants, basis): def dope(self, dopants, basis):
...@@ -109,3 +110,26 @@ class AseSim: ...@@ -109,3 +110,26 @@ class AseSim:
if self.dyn.atoms.get_atomic_numbers()[nbor_idx] == 0: if self.dyn.atoms.get_atomic_numbers()[nbor_idx] == 0:
empty_nbor_list[idx] += 1 empty_nbor_list[idx] += 1
self.dyn.atoms.set_array('empty_nbors', empty_nbor_list) self.dyn.atoms.set_array('empty_nbors', empty_nbor_list)
def tag_elements(self):
"""
Change the tags of random elements in the lattice so they can be tracked
:return: None
"""
tag_target = 5 # how many atoms to tag of each element
number_list = self.atoms.get_atomic_numbers()
element_list = np.unique(number_list)
tag_list = self.atoms.get_tags()
tagged_counts = {} # dict of amount of tagged atoms per element
tag_index = 1
while sum(tagged_counts.values()) < tag_target * len(element_list):
rand_idx = np.random.randint(len(self.atoms))
rand_number = number_list[rand_idx]
tagged_count = tagged_counts.get(rand_number, 0)
if not tagged_count >= tag_target:
tag_list[rand_idx] = tag_index
self.tagged_elements[tag_index] = rand_number
tag_index += 1
tagged_counts[rand_number] = tagged_count + 1
self.atoms.set_tags(tag_list)
...@@ -97,7 +97,7 @@ class SajuhakDynamics(Dynamics): ...@@ -97,7 +97,7 @@ class SajuhakDynamics(Dynamics):
self.nbor_list = nbor_list self.nbor_list = nbor_list
# self.nbor_list.update(self.atoms) # self.nbor_list.update(self.atoms)
self.rand_ox = rand_ox self.rand_ox = rand_ox
self.diff_const_dict = {} self.tag_count_list = {}
def run(self, kT, n_steps=100): def run(self, kT, n_steps=100):
for i in range(n_steps): for i in range(n_steps):
...@@ -111,38 +111,26 @@ class SajuhakDynamics(Dynamics): ...@@ -111,38 +111,26 @@ class SajuhakDynamics(Dynamics):
rand_idx = np.random.randint(len(self.atoms)) rand_idx = np.random.randint(len(self.atoms))
rand_number = self.atoms.get_atomic_numbers()[rand_idx] rand_number = self.atoms.get_atomic_numbers()[rand_idx]
rand_symbol = ase.atoms.chemical_symbols[rand_number] rand_symbol = ase.atoms.chemical_symbols[rand_number]
if rand_symbol not in self.diff_const_dict.keys():
self.diff_const_dict[rand_symbol] = [0, 0]
self.diff_const_dict[rand_symbol][1] += 1
nbor_indices = self.nbor_list.neighbors[rand_idx] nbor_indices = self.nbor_list.neighbors[rand_idx]
nbor_idx = np.random.choice(nbor_indices) nbor_idx = np.random.choice(nbor_indices)
nbor_number = self.atoms.get_atomic_numbers()[nbor_idx] nbor_number = self.atoms.get_atomic_numbers()[nbor_idx]
nbor_symbol = ase.atoms.chemical_symbols[nbor_number] nbor_symbol = ase.atoms.chemical_symbols[nbor_number]
if nbor_symbol not in self.diff_const_dict.keys():
self.diff_const_dict[nbor_symbol] = [0, 0]
self.diff_const_dict[nbor_symbol][1] += 1
if rand_number == nbor_number: if rand_number == nbor_number:
# if the two atoms are the same, nothing changes, remove from total exchanges # if the two atoms are the same, nothing changes
self.diff_const_dict[rand_symbol][1] -= 1
self.diff_const_dict[nbor_symbol][1] -= 1
return return
# if either point is empty, initiate random (de)oxidation # if either point is empty, initiate random (de)oxidation
elif self.rand_ox and (rand_number == 0 or nbor_number == 0): elif self.rand_ox and (rand_number == 0 or nbor_number == 0):
# if either point is not oxygen, initiate random oxidation # if either point is not oxygen, initiate random oxidation
if rand_number != 8 and nbor_number != 8: if rand_number != 8 and nbor_number != 8:
if self.random_oxidation(kT, rand_idx, nbor_idx): if self.random_oxidation(kT, rand_idx, nbor_idx):
# if the oxidation was successful, don't swap these atoms yet, remove from total exchanges # if the oxidation was successful, don't swap these atoms yet
self.diff_const_dict[rand_symbol][1] -= 1
self.diff_const_dict[nbor_symbol][1] -= 1
return return
# else initiate random deoxidation # else initiate random deoxidation
else: else:
if self.random_deoxidation(kT, rand_idx, nbor_idx): if self.random_deoxidation(kT, rand_idx, nbor_idx):
# if the deoxidation was successful, don't swap these atoms yet, remove from total exchanges # if the deoxidation was successful, don't swap these atoms yet
self.diff_const_dict[rand_symbol][1] -= 1
self.diff_const_dict[nbor_symbol][1] -= 1
return return
energy_start = self.calc_energy_at_points([rand_idx, nbor_idx]) energy_start = self.calc_energy_at_points([rand_idx, nbor_idx])
...@@ -154,8 +142,14 @@ class SajuhakDynamics(Dynamics): ...@@ -154,8 +142,14 @@ class SajuhakDynamics(Dynamics):
if not np.random.random() < exp(-delta_e / kT): if not np.random.random() < exp(-delta_e / kT):
self.swap_atoms(nbor_idx, rand_idx) self.swap_atoms(nbor_idx, rand_idx)
else: else:
self.diff_const_dict[rand_symbol][0] += 1 # swap tags
self.diff_const_dict[nbor_symbol][0] += 1 tag_list = self.atoms.get_tags()
rand_tag = tag_list[rand_idx]
self.tag_count_list[rand_tag] = self.tag_count_list.get(rand_tag, 0) + 1
nbor_tag = tag_list[nbor_idx]
self.tag_count_list[nbor_tag] = self.tag_count_list.get(nbor_tag, 0) + 1
tag_list[rand_idx], tag_list[nbor_idx] = tag_list[nbor_idx], tag_list[rand_idx]
self.atoms.set_tags(tag_list)
def swap_atoms(self, index1, index2): def swap_atoms(self, index1, index2):
atom1_number = self.atoms[index1].number atom1_number = self.atoms[index1].number
......
...@@ -13,6 +13,7 @@ from collections import OrderedDict ...@@ -13,6 +13,7 @@ from collections import OrderedDict
# import cProfile # import cProfile
import ase_sim import ase_sim
from ase.data import chemical_symbols
import vis import vis
...@@ -175,6 +176,7 @@ class SimController(object): ...@@ -175,6 +176,7 @@ class SimController(object):
self.sim.create_lattice(**self.lattice_params, rand_ox=self.sim_params['rand_ox']) self.sim.create_lattice(**self.lattice_params, rand_ox=self.sim_params['rand_ox'])
self.bulk_counts = self.sim.dope(self.bulk_dopants, self.sim.basis) self.bulk_counts = self.sim.dope(self.bulk_dopants, self.sim.basis)
self.empty_space_counts = self.sim.dope(self.empty_space_dopants, 'X') self.empty_space_counts = self.sim.dope(self.empty_space_dopants, 'X')
self.sim.tag_elements()
def run(self): def run(self):
if type(self.sim_params['kT']) is list: if type(self.sim_params['kT']) is list:
...@@ -223,23 +225,22 @@ class SimController(object): ...@@ -223,23 +225,22 @@ class SimController(object):
lattice_counts = bulk_counts_header + bulk_counts_str + vacuum_counts_header + vacuum_counts_str lattice_counts = bulk_counts_header + bulk_counts_str + vacuum_counts_header + vacuum_counts_str
diff_const_header = "Exchanges per element (successful/total (percentage)):\n" diff_const_header = "Distances traveled by tagged atoms (average, list):\n"
diff_const_line = " {symbol}: {successful}/{total} ({perc:.2f}%)\n" diff_const_line = " {symbol}: {average}, {list}\n"
diff_const_str = "" diff_const_str = ""
# tot_exchanges = self.sim_params['n_exchanges'] counts_by_element = {}
# if type(tot_exchanges) is list: for index, number in self.sim.tagged_elements.items():
# tot_exchanges = sum(tot_exchanges) count = self.sim.dyn.tag_count_list.get(index, 0)
if number not in counts_by_element.keys():
diff_const_dict_items_sorted_by_total = sorted(self.sim.dyn.diff_const_dict.items(), counts_by_element[number] = []
key=lambda item: item[1][1]) counts_by_element[number].append(count)
for symbol, success_tot in diff_const_dict_items_sorted_by_total:
if symbol == 'X': for number, counts in sorted(counts_by_element.items()):
symbol = 'Vac' if number != 0:
diff_const_str += diff_const_line.format(symbol=symbol, diff_const_str += diff_const_line.format(symbol=chemical_symbols[number],
successful=success_tot[0], average=np.mean(counts),
total=success_tot[1], list=counts)
perc=success_tot[0] / success_tot[1] * 100)
diff_const = diff_const_header + diff_const_str diff_const = diff_const_header + diff_const_str
......
...@@ -26,15 +26,17 @@ def read_xyz_frame(xyz_file_in): ...@@ -26,15 +26,17 @@ def read_xyz_frame(xyz_file_in):
list_properties = [] list_properties = []
comment = xyz_file_in.readline() comment = xyz_file_in.readline()
info = ase.io.extxyz.key_val_str_to_dict(comment) info = ase.io.extxyz.key_val_str_to_dict(comment)
# properties, names, dtype, convs = ase.io.extxyz.parse_properties(info['Properties']) _, names, _, _ = ase.io.extxyz.parse_properties(info['Properties'])
# read the next n_sites lines, the index is not used # read the next n_sites lines, the index is not used
for _ in range(n_sites): for _ in range(n_sites):
# an extxyz line is "element x y z properties"
line = xyz_file_in.readline() line = xyz_file_in.readline()
if info['Properties'].find('empty_nbors:I:1') > info['Properties'].find('Z:I:1'): values = line.split()
element, x, y, z, _, empty_nbors = line.split() element, x, y, z = values[:4]
else: values = values[4:]
element, x, y, z, empty_nbors, _ = line.split() row = {}
for i, name in enumerate(names[2:]):
row[name] = values[i]
empty_nbors = row['empty_nbors']
# add to the list of particles # add to the list of particles
list_properties.append((element, float(x), float(y), float(z), int(empty_nbors))) list_properties.append((element, float(x), float(y), float(z), int(empty_nbors)))
# return the list of particles # return the list of particles
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment