import random
import itertools
import numpy as np
from pychemia.utils.mathematics import apply_rotation
[docs]class StructureChanger:
def __init__(self, structure):
self.old_structure = structure
self.new_structure = structure.copy()
self.operations = []
[docs] def permutator(self, pair):
self.new_structure.symbols[pair[0]] = self.old_structure.symbols[pair[1]]
self.new_structure.symbols[pair[1]] = self.old_structure.symbols[pair[0]]
self.operations.append({'permutator': (pair[0], pair[1])})
[docs] def random_permutator(self, forbidden_list=None):
assert (self.old_structure.nspecies > 1)
pairs = list(itertools.combinations(self.old_structure.species, 2))
if forbidden_list is None:
flist = []
else:
flist = forbidden_list
for pair in pairs:
for fpair in flist:
if pair == tuple(fpair) or pair == tuple(reversed(fpair)):
pairs.remove(pair)
pair = random.choice(pairs)
indices0 = [i for i, x in enumerate(self.old_structure.symbols) if x == pair[0]]
index0 = random.choice(indices0)
indices1 = [i for i, x in enumerate(self.old_structure.symbols) if x == pair[1]]
index1 = random.choice(indices1)
self.permutator((index0, index1))
[docs] def move_one_atom(self, index, vector):
self.new_structure.positions[index] += vector
self.new_structure.positions2reduced()
self.operations.append({'move_one_atom': (index, vector)})
[docs] def random_move_one_atom(self, mu=0.1, sigma=0.01):
index = random.randint(0, len(self.old_structure) - 1)
radius = np.abs(np.random.normal(mu, sigma))
theta_x = 2 * np.pi * np.random.random_sample()
theta_y = 2 * np.pi * np.random.random_sample()
theta_z = 2 * np.pi * np.random.random_sample()
vector = apply_rotation([1, 0, 0], theta_x, theta_y, theta_z)
self.move_one_atom(index, vector * radius)
[docs] def random_move_many_atoms(self, epsilon=0.01, forbidden_indices=None, forbidden_species=None):
if forbidden_indices is None:
findices = []
else:
findices = forbidden_indices
if forbidden_species is None:
fspec = []
else:
fspec = forbidden_species
for iatom in range(len(self.old_structure)):
if iatom not in findices and self.new_structure.symbols[iatom] not in fspec:
self.random_move_one_atom(epsilon)
[docs] def random_change(self, epsilon):
rnd = np.random.random()
if rnd < 0.25:
self.random_deform_cell(maxdelta=epsilon)
elif rnd < 0.5:
self.random_move_many_atoms(epsilon=epsilon)
elif rnd < 0.75 and self.old_structure.nspecies > 1:
self.random_permutator()
else:
self.random_move_one_atom(mu=epsilon, sigma=0.01)