Module ctoolkit.tools.cellOperations
Expand source code
from toolkit.global_vars.ext_libs import *
class cellOperations:
def __init__(self):
pass
@calculate_time
def func3(self):
time.sleep(3)
@calculate_time
def frac_to_cart(self, B, atom_set):
new_atom_set = np.dot(B, atom_set.T).T
return new_atom_set
@calculate_time
def cart_to_frac(self, B, atom_set):
Binv = np.linalg.inv(B)
new_atom_set = np.dot(Binv, atom_set.T).T
return new_atom_set
def rigid_displacement(self, vector, structure):
new_cart = np.zeros([len(structure.at_cart), 3], dtype=float)
new_frac = np.zeros([len(structure.at_frac), 3], dtype=float)
vector = np.array(vector, dtype=float)
disp = np.zeros([3], dtype=float)
for i in range(3):
disp += vector[i]*structure.B[i,:]
for atom in range(len(structure.at_cart)):
new_cart[atom] = np.copy(structure.at_cart[atom] + disp)
structure.at_cart = np.copy(new_cart)
structure.recalculate_poscar()
def apply_rotation(self, vector, angle, axis, origin=None):
# The idea:
# We want to rotate a group of atoms. The positions
# of the atoms are given as vectors from the crystallographic
# origin of coordinates. Thus what we want to do is:
# move origin -> apply rotation along given axis -> go back to crystal. origin
# We use the tensorial form of the rotation operator
axis = np.array(axis)
axis = axis/(np.sqrt(np.dot(axis, axis)))
cross = np.zeros([3,3], dtype=float)
cart = np.array([[1.0, .0, .0], [.0, 1.0, .0], [.0, .0, 1.0]], dtype=float)
for i in range(3):
c1 = np.cross(axis, cart[i])
cross += np.outer(c1, cart[i])
R = np.cos(angle)*np.eye(3,3) + (np.sin(angle))*(cross) + (1-np.cos(angle))*(np.outer(axis, axis))
# Now we move the origin, compute, and go back to the crystal. origin. All packed.
if origin is None:
origin = np.zeros([3], dtype=float)
return np.dot(R, vector - origin) + origin
def rigid_rotation(self, vector_list, angle, axis, origin=None):
if len(vector_list.shape) < 2:
return self.apply_rotation(vector_list, angle, axis, origin)
else:
veclist = []
for i in range(len(vector_list)):
veclist.append(self.apply_rotation(vector_list[i], angle, axis, origin))
return np.array(veclist, dtype=float)
def new_supercell_VASP(self, N, VASP_struct):
new_VASP = self.copy_structure(VASP_struct)
# Objects can be different...
new_structure = new_VASP.POSCAR
new_structure.at_frac = np.zeros([np.prod(N)*len(VASP_struct.POSCAR.at_frac), 3])
ct_atom = 0
ct2 = 0
new_structure.namelist = ['']
new_structure.atom_id = []
for specie_id, specie in enumerate(VASP_struct.POSCAR.names):
new_structure.multiplicity[specie_id] = VASP_struct.POSCAR.multiplicity[specie_id]*np.prod(N)
for atom in range(VASP_struct.POSCAR.multiplicity[specie_id]):
#for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]:
#for atom in range(structure.multiplicity[specie_id]):
for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]: #This inverts how the atoms are printed
Id = np.eye(3)
cell_vector = nx*Id[0] + ny*Id[1] + nz*Id[2]
# Frac coordinates need to be reescaled to the new supercell
temp_vec = np.copy(VASP_struct.POSCAR.at_frac[ct2] + cell_vector)
for i in range(3):
new_structure.at_frac[ct_atom, i] = temp_vec[i]/N[i]
new_structure.namelist.append(VASP_struct.POSCAR.namelist[ct2])
new_structure.atom_id.append(VASP_struct.POSCAR.atom_id[ct2])
ct_atom += 1
ct2 += 1
for i in range(3):
new_structure.B[i] = np.copy(VASP_struct.POSCAR.B[i]*N[i])
new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac))
mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac))
new_structure.volume *= mult
#new_structure.energy *= mult
return new_structure
def new_supercell(self, N, structure):
# We select and divide into smaller functions as a sort of override
if structure.type == 'VASP':
new_structure = self.new_supercell_VASP(N, structure)
return new_structure
# Add atoms given a vector
def spawn_cell_frac(self, vector, structure):
new_frac = np.copy(structure.at_frac)
for i in range(len(new_frac)):
new_frac[i] = structure.at_frac[i] + np.array(vector, dtype=float)
return np.copy(new_frac)
def spawn_cell_invfrac(self, vector, structure):
new_frac = np.copy(structure.at_frac)
tol = 0.02
for i in range(len(new_frac)):
"""
temp_vec = -structure.at_frac[i] + np.array([1,1,1], dtype=float)
# Correct frac on the fly
for i in range(3):
if(np.abs(temp_vec[i]-1.0) < tol):
temp_vec[i] -= 0.0#1.0
#new_frac[i] = temp_vec + np.array(vector, dtype=float)
"""
# We go to [-1,-1,-1] and then invert!
temp_vec = -(structure.at_frac[i] + np.array([-1,-1,-1], dtype=float))
# Correct frac on the fly
for j in range(3):
if(np.abs(temp_vec[j]-1.0) < tol):
temp_vec[j] -= 1.0
new_frac[i] = temp_vec + np.array(vector, dtype=float)
#new_frac[i] = np.array(vector, dtype=float)-structure.at_frac[i] + np.array([-1,-1,-1], dtype=float)
return np.copy(new_frac)
def centrosymmetric_supercell_VASP(self, VASP_structure):
new_VASP = self.copy_structure(VASP_structure)
new_structure = new_VASP.POSCAR
# Centrosymmetry requires an atom well located for the origin.
# Thus we need to shift to set the zero (for now experimental)
shifted_structure = self.copy_structure(structure).POSCAR
for i in range(len(shifted_structure.at_frac)):
shifted_structure.at_frac[i] -= structure.at_frac[0]
cells = []
"""
for i, j in [(i,j) for i in range(2) for j in range(2)]:
cells.append(self.spawn_cell_frac([i,j,0], shifted_structure))
for i, j in [(i,j) for i in range(2) for j in range(2)]:
cells.append(self.spawn_cell_invfrac([i,j,1], shifted_structure))
"""
# Test: only 2 cells
cells.append(self.spawn_cell_frac([0,0,0], shifted_structure))
cells.append(self.spawn_cell_invfrac([0,0,1], shifted_structure))
N = [1,1,2]
ct = 0
ct2 = 0
new_structure.at_frac = np.zeros([np.prod(N)*len(structure.at_frac), 3])
new_structure.namelist = ['']
new_structure.atom_id = []
for specie_id, specie in enumerate(structure.names):
new_structure.multiplicity[specie_id] = structure.multiplicity[specie_id]*np.prod(N)
for atom in range(structure.multiplicity[specie_id]):
for cell in cells:
# Frac coordinates need to be reescaled to the new supercell
temp_vec = np.copy(cell[ct2])
for i in range(3):
new_structure.at_frac[ct, i] = temp_vec[i]/N[i]
new_structure.atom_id.append(structure.atom_id[ct2])
new_structure.namelist.append(structure.namelist[ct2])
ct += 1
ct2 += 1
for i in range(3):
new_structure.B[i] = np.copy(structure.B[i]*N[i])
new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac))
mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac))
new_structure.volume *= mult
new_structure.energy *= mult
return new_structure
def centrosymmetric_supercell(self, structure):
# We select and divide into smaller functions as a sort of override
if structure.type == 'VASP':
new_structure = self.new_supercell_VASP(N, structure)
return new_structure
def interpolate_structures_VASP(self, struct1, struct2, num_images):
structures = []
structures.append(struct1)
for i in range(1, num_images+1):
# Load structure + cleanup of frac coordinates
new_VASP = self.copy_structure(struct1)
new_structure = new_VASP.POSCAR
new_structure.frac_image_sniper()
# Linear interpolation of cell & frac & cart
new_structure.B = struct1.POSCAR.B + float(i)/(num_images+2)*(struct2.POSCAR.B-struct1.POSCAR.B)
new_structure.at_frac = struct1.POSCAR.at_frac + float(i)/(num_images+2)*(struct2.POSCAR.at_frac-struct1.POSCAR.at_frac)
# Could put a control for running images here
new_structure.at_cart = self.frac_to_cart(new_structure.B, new_structure.at_frac)#struct1.at_cart + float(i/(num_images+2))*(struct2.at_cart-struct1.at_cart)
# Probably useless or troublesome:
new_structure.frac_image_sniper()
structures.append(new_VASP)
structures.append(struct2)
return structures
def interpolate_structures(self, struct1, struct2, num_images):
# We select and divide into smaller functions as a sort of override
if struct1.type != struct2.type:
print('Error! Structures in different format. Transform them first!')
sys.exit()
# Attempt conversion
#print('Attempting to unify types...')
#struct1 = self.transform_type(struct1, struct2.type)
#print("Successfully transformed type of structure 1 (%s) into %s!" % (struct1.type, struct2.type))
if struct1.type == 'VASP':
structures = self.interpolate_structures_VASP(struct1, struct2, num_images)
return structures
@calculate_time
def find_firstneighbors(self, atom, satellites, basis):
d = np.zeros([len(satellites)], dtype=float)
tol = 20 # in percentage
for i in range(len(satellites)):
best_transform, transform_key = self.findBestPairPeriodic(atom, satellites[i], basis)
dist = np.sqrt(np.dot(atom-best_transform, atom-best_transform))
# We exclude the "self" atom
if(dist > 0.0):
d[i] = dist
else:
d[i] = 1E5
#print(np.sort(d))
min_dist = np.min(d)
list_neighbors = []
for i in range(len(satellites)):
if d[i] < min_dist*(1+tol/100):
#print(i, d[i], min_dist*(1+tol/100))
list_neighbors.append(i)
return list_neighbors
def find_full_molecules(self, struct, center, satellite, num_neighbors=0):
center_atoms = struct.filter_atoms(center)
sat_atoms = struct.filter_atoms(satellite)
all_atoms = np.copy(struct.at_cart)
center_atoms_list = struct.filter_atoms_list(center)
sat_atoms_list = struct.filter_atoms_list(satellite)
groups = []
nice_molecules = []
for i in range(len(center_atoms)):
nice_molecule = []
sat_atom_neighbors = []
list_neighbors = self.find_firstneighbors(center_atoms[i], sat_atoms, struct.B)
for j in list_neighbors:
sat_atom_neighbors.append(sat_atoms_list[j])
if num_neighbors==1:
# center atom new first neighbors
cfn_bulk = self.find_firstneighbors(center_atoms[i], all_atoms, struct.B)
cfn_list = []
for cfn in cfn_bulk:
if ((cfn not in center_atoms_list) and
(cfn not in sat_atom_neighbors)):
cfn_list.append(cfn)
# find the first neighbors of every satellite
sfn_list_col = []
for j in sat_atom_neighbors:
sfn_bulk = self.find_firstneighbors(struct.at_cart[j], all_atoms, struct.B)
sfn_list = []
for sfn in sfn_bulk:
if ((sfn not in center_atoms_list) and
(sfn not in sat_atom_neighbors) and
(sfn not in cfn_list)):
sfn_list.append(sfn)
sfn_list_col.append(sfn_list)
groups.append([center_atoms_list[i], sat_atom_neighbors, cfn_list, sfn_list_col])
nice_molecule.append(center_atoms_list[i])
for sat in sat_atom_neighbors: nice_molecule.append(sat)
for cfn in cfn_list: nice_molecule.append(cfn)
for sfn in sfn_list: nice_molecule.append(sfn)
nice_molecules.append(nice_molecule)
else:
groups.append([center_atoms_list[i], sat_atom_neighbors])
sys.stdout.write("%3.3f%%\r" % (100*(i+1)/len(center_atoms)))
sys.stdout.flush()
return groups, nice_molecules
# Find groups of atoms between the CENTER
# of the interaction and the SATELLITE possible
# atoms belonging to a particular species
@calculate_time
def find_groups(self, struct, center, satellite, excludeSatList=[]):
center_atoms = struct.filter_atoms(center)
sat_atoms = struct.filter_atoms(satellite, excludeSatList=excludeSatList)
center_atoms_list = struct.filter_atoms_list(center)
sat_atoms_list = struct.filter_atoms_list(satellite, excludeSatList=excludeSatList)
#print(len(sat_atoms), len(sat_atoms_list))
groups = []
for i in range(len(center_atoms)):
sat_atom_neighbors = []
list_neighbors = self.find_firstneighbors(center_atoms[i], sat_atoms, struct.B)
for j in list_neighbors:
sat_atom_neighbors.append(sat_atoms_list[j])
groups.append([center_atoms_list[i], sat_atom_neighbors])
#sys.stdout.write("%3.3f%%\r" % (100*(i+1)/len(center_atoms)))
#sys.stdout.flush()
return groups
# Sometimes atoms go out of the simulation box. Bound box. Whatever.
# And sometimes we want them out to understand the structure.
# So I add here a wrapper for:
# 1) Application of periodic boundary conditions.
# 2) Unbound atoms.
# The code here works in fractional coordinates.
# You can provide a structure, or a set of atoms,
# or a cartesian set of atoms + basis...
# Or at least, it will do that. For now, at_frac.
@calculate_time
def handle_periodic_boundaries(self, at_frac, mode='bound'):
if mode != 'bound' or mode != 'unbound':
print("PBC handler: using mode 'bound' by default (input not recognized)")
mode = 'bound'
#CONTINUE_HERE
# This function searches for a periodic image of periodic_atom
# that is the nearest to fixed_atom. Returns the best periodic
# image found, and the key [k_x, k_y, k_z] necessary to transform it.
@calculate_time
def findBestPairPeriodic_test(self, fixed_atom, periodic_atom, basis):
mindist = 1E3
best_transform = np.copy(periodic_atom)
best_transform_key = np.array([0,0,0], dtype=float)
# We search 1 cell around the boundary
# Maybe we can search in a decomposed way i>j>k?????
for axis in range(3):
transform_key = np.copy(best_transform_key)
for i in range(-1, 2):
transform_key[axis] = i
transformed_atom = periodic_atom + basis[0]*transform_key[0] + basis[1]*transform_key[1] + basis[2]*transform_key[2]
vec = fixed_atom-transformed_atom
dist = np.sqrt(np.dot(vec, vec))
if (dist-mindist) < 0.0:
mindist = dist + 0.0
best_transform = np.copy(transformed_atom)
best_transform_key[axis] = i
return best_transform, best_transform_key
@calculate_time
def findBestPairPeriodic_GPT(self, fixed_atom, periodic_atom, basis):
translations = np.array([i*basis[0] + j*basis[1] + k*basis[2] for i in range(-1,2) for j in range(-1,2) for k in range(-1,2)])
#transformed_atoms = periodic_atom + translations
#print(translations)
transformed_atoms = np.array([periodic_atom + translations[i] for i in range(len(translations))])
#print(transformed_atoms)
#vec = fixed_atom - transformed_atoms
vec = np.array([fixed_atom + transformed_atoms[i] for i in range(len(transformed_atoms))])
dist = np.linalg.norm(vec, axis=1)
min_index = np.argmin(dist)
#print(np.min(dist), min_index)
return transformed_atoms[min_index], [min_index//9-1, (min_index//3)%3-1, min_index%3-1]
# This function searches for a periodic image of periodic_atom
# that is the nearest to fixed_atom. Returns the best periodic
# image found, and the key [k_x, k_y, k_z] necessary to transform it.
@calculate_time
def findBestPairPeriodic(self, fixed_atom, periodic_atom, basis):
mindist = 1E3
best_transform = np.copy(periodic_atom)
transform_key = [0,0,0]
# We search 1 cell around the boundar
"""
bi, bj, bk = 0, 0, 0
for i in range(-1, 2):
transformed_atom = periodic_atom + basis[0]*float(i) + basis[1]*float(bj) + basis[2]*float(bk)
vec = fixed_atom-transformed_atom
dist = np.sqrt(np.dot(vec, vec))
try:
if (dist-mindist) < 0.0:
mindist = dist
best_transform = np.copy(transformed_atom)
bi = i
except:
print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec)
sys.exit()
for j in range(-1, 2):
transformed_atom = periodic_atom + basis[0]*float(bi) + basis[1]*float(j) + basis[2]*float(bk)
vec = fixed_atom-transformed_atom
dist = np.sqrt(np.dot(vec, vec))
try:
if (dist-mindist) < 0.0:
mindist = dist
best_transform = np.copy(transformed_atom)
bj = j
except:
print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec)
sys.exit()
for k in range(-1, 2):
transformed_atom = periodic_atom + basis[0]*float(bi) + basis[1]*float(bj) + basis[2]*float(k)
vec = fixed_atom-transformed_atom
dist = np.sqrt(np.dot(vec, vec))
try:
if (dist-mindist) < 0.0:
mindist = dist
best_transform = np.copy(transformed_atom)
bk = k
except:
print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec)
sys.exit()
transform_key = [bi, bj, bk]
"""
for i in range(-1, 2):
for j in range(-1, 2):
for k in range(-1, 2):
transformed_atom = periodic_atom + basis[0]*float(i) + basis[1]*float(j) + basis[2]*float(k)
vec = fixed_atom-transformed_atom
dist = np.sqrt(np.dot(vec, vec))
#print(transformed_atom, dist, mindist, best_transform)
try:
if (dist-mindist) < 0.0:
mindist = dist
best_transform = np.copy(transformed_atom)
transform_key = [i, j, k]
except:
print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec)
sys.exit()
return best_transform, transform_key
@calculate_time
def atom_transform(self, atom, basis, key):
return atom + basis[0]*float(key[0]) + basis[1]*float(key[1]) + basis[2]*float(key[2])
# Function to find pairs of atoms. Input can be in standard .at_frac
# Returns the pair[atom1]=atom2 and the keyring [k_x, k_y, k_z] for the periodic images.
@calculate_time
def find_pairs(self, atoms1, atoms2, box2):
pair = np.zeros([len(atoms1)], dtype=int)
pair[:] -= 1
mindist_pair = np.zeros([len(atoms1)], dtype=float)
mindist_pair[:] -= 1
keyring = []
listat2 = list(range(len(atoms2)))
for i in range(len(atoms1)):
threshold = 1E3 # This is just an init parameter
for j in listat2:
atoms2periodic, transform_key = self.findBestPairPeriodic_test(atoms1[i], atoms2[j], box2)
dvec = np.copy(atoms1[i]-atoms2periodic)
dist = np.sqrt(np.dot(dvec, dvec))
# This is unclean but efficient. Basically, checks that the new transformation
# provides a smaller distance between pairs of atoms. Probably can be cleaned.
if (dist-threshold) < 0.0:
threshold = dist + 0.0
pair[i] = j
best_transform_key = transform_key
if pair[i] in listat2: listat2.remove(pair[i])
keyring.append(best_transform_key)
mindist_pair[i] = threshold + 0.0
for i, a2 in enumerate(pair):
# This warning should be set to an actual threshold parameter
if mindist_pair[i] > 2.5 and a2 > -1: print("WARNING with pair %d %d min distance: %.5f" % (i, a2, mindist_pair[i]))
#print("Pair %d %d: %.5f" % (i, a2, mindist_pair[i]), atoms1[i], atoms2[a2], keyring[i]) # DEBUG
return pair, keyring
# A quicker function to find keys of known pairs of atoms
@calculate_time
def refresh_key(self, atoms1, atoms2, box2, pairs):
key = []
for at1, at2 in enumerate(pairs):
atoms2periodic, transform_key = self.findBestPairPeriodic_test(atoms1[at1], atoms2[at2], box2)
key.append(transform_key)
return key
# Key is for the atom 2.
@calculate_time
def build_vectormap(self, atoms1, atoms2, box, pairs, initial_key):
vectors = np.zeros([len(atoms1), 3], dtype=float)
key = initial_key + .0
d = []
for i, j in enumerate(pairs):
atoms2_pos_correction = key[i][0]*box[0] + key[i][1]*box[1] + key[i][2]*box[2]
vec = atoms1[i] - (atoms2[j] + atoms2_pos_correction)
vectors[i] = np.copy(vec)
d.append(np.sqrt(np.dot(vec,vec)))
#print(d[-1])
# The tolerance should probably be a parameter
if (np.max(np.array(d, dtype=float)) > 2.5):
key = self.refresh_key(atoms1, atoms2, box, pairs)
d = []
for i, j in enumerate(pairs):
atoms2_pos_correction = key[i][0]*box[0] + key[i][1]*box[1] + key[i][2]*box[2]
vec = atoms1[i] - (atoms2[j] + atoms2_pos_correction)
vectors[i] = np.copy(vec)
dist = np.sqrt(np.dot(vec, vec))
d.append(dist)
# Just a check
if (np.max(np.array(d, dtype=float)) > 2.5):
print("Max distance between atoms is larger than 2.5")
sys.exit()
return vectors, key
def compute_angles_MD_GPT(self, vectors, vec_of_vecs=False):
num_steps, num_vecs = vectors.shape[:2]
e_x = np.array([1, 0, 0], dtype=np.float32)
e_z = np.array([0, 0, 1], dtype=np.float32)
v_pbc = vectors - np.einsum('ij,j->ij', vectors, e_x) * e_x
v_pab = vectors - np.einsum('ij,j->ij', vectors, e_z) * e_z
norm_vpbc = np.linalg.norm(v_pbc, axis=-1)
norm_vpab = np.linalg.norm(v_pab, axis=-1)
phi = np.arctan2(np.cross(v_pab, e_z), np.dot(v_pab, e_x))
theta = np.arccos(np.einsum('ij,j->i', vectors, e_z) / norm_vpbc)
ctheta = np.cos(theta)
if vec_of_vecs:
angles = np.concatenate((phi.reshape(-1,1), theta.reshape(-1,1)), axis=1)
return angles.reshape(num_steps, num_vecs, 2)
else:
return phi, theta, ctheta
@calculate_time
def compute_angles_MD(self, vectors, vec_of_vecs=False):
num_steps = len(vectors)
num_vecs = len(vectors[0])
theta = np.zeros([num_steps*num_vecs], dtype=float)
phi = np.zeros([num_steps*num_vecs], dtype=float)
ctheta = np.zeros([num_steps*num_vecs], dtype=float)
e_x = np.eye(3, dtype=float)[0,:]
e_z = np.eye(3, dtype=float)[2,:]
angles = []
for i in range(num_steps):
angles_inner = []
for j in range(num_vecs):
vec = np.copy(vectors[i,j])
v_pbc = vec - np.dot(vec, e_x)*e_x
v_pab = vec - np.dot(vec, e_z)*e_z
norm_vpbc = np.sqrt(np.dot(v_pbc, v_pbc))
norm_vpab = np.sqrt(np.dot(v_pab, v_pab))
# Angles = [\phi, \theta]
phi[i*num_vecs+j] = self.calculate_angle_full(e_x, v_pab/norm_vpab, e_z) + 0.0
theta[i*num_vecs+j] = self.calculate_angle(e_z, vec)#angle(e_z, v_pbc/norm_vpbc, e_x) + 0.0
ctheta[i*num_vecs+j] = np.cos(theta[i*num_vecs+j])
angles_inner.append([phi[i*num_vecs+j], theta[i*num_vecs+j]])
angles.append(angles_inner)
angles = np.array(angles)
if vec_of_vecs == False:
return phi, theta, ctheta
else:
return angles
Classes
class cellOperations
-
Expand source code
class cellOperations: def __init__(self): pass @calculate_time def func3(self): time.sleep(3) @calculate_time def frac_to_cart(self, B, atom_set): new_atom_set = np.dot(B, atom_set.T).T return new_atom_set @calculate_time def cart_to_frac(self, B, atom_set): Binv = np.linalg.inv(B) new_atom_set = np.dot(Binv, atom_set.T).T return new_atom_set def rigid_displacement(self, vector, structure): new_cart = np.zeros([len(structure.at_cart), 3], dtype=float) new_frac = np.zeros([len(structure.at_frac), 3], dtype=float) vector = np.array(vector, dtype=float) disp = np.zeros([3], dtype=float) for i in range(3): disp += vector[i]*structure.B[i,:] for atom in range(len(structure.at_cart)): new_cart[atom] = np.copy(structure.at_cart[atom] + disp) structure.at_cart = np.copy(new_cart) structure.recalculate_poscar() def apply_rotation(self, vector, angle, axis, origin=None): # The idea: # We want to rotate a group of atoms. The positions # of the atoms are given as vectors from the crystallographic # origin of coordinates. Thus what we want to do is: # move origin -> apply rotation along given axis -> go back to crystal. origin # We use the tensorial form of the rotation operator axis = np.array(axis) axis = axis/(np.sqrt(np.dot(axis, axis))) cross = np.zeros([3,3], dtype=float) cart = np.array([[1.0, .0, .0], [.0, 1.0, .0], [.0, .0, 1.0]], dtype=float) for i in range(3): c1 = np.cross(axis, cart[i]) cross += np.outer(c1, cart[i]) R = np.cos(angle)*np.eye(3,3) + (np.sin(angle))*(cross) + (1-np.cos(angle))*(np.outer(axis, axis)) # Now we move the origin, compute, and go back to the crystal. origin. All packed. if origin is None: origin = np.zeros([3], dtype=float) return np.dot(R, vector - origin) + origin def rigid_rotation(self, vector_list, angle, axis, origin=None): if len(vector_list.shape) < 2: return self.apply_rotation(vector_list, angle, axis, origin) else: veclist = [] for i in range(len(vector_list)): veclist.append(self.apply_rotation(vector_list[i], angle, axis, origin)) return np.array(veclist, dtype=float) def new_supercell_VASP(self, N, VASP_struct): new_VASP = self.copy_structure(VASP_struct) # Objects can be different... new_structure = new_VASP.POSCAR new_structure.at_frac = np.zeros([np.prod(N)*len(VASP_struct.POSCAR.at_frac), 3]) ct_atom = 0 ct2 = 0 new_structure.namelist = [''] new_structure.atom_id = [] for specie_id, specie in enumerate(VASP_struct.POSCAR.names): new_structure.multiplicity[specie_id] = VASP_struct.POSCAR.multiplicity[specie_id]*np.prod(N) for atom in range(VASP_struct.POSCAR.multiplicity[specie_id]): #for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]: #for atom in range(structure.multiplicity[specie_id]): for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]: #This inverts how the atoms are printed Id = np.eye(3) cell_vector = nx*Id[0] + ny*Id[1] + nz*Id[2] # Frac coordinates need to be reescaled to the new supercell temp_vec = np.copy(VASP_struct.POSCAR.at_frac[ct2] + cell_vector) for i in range(3): new_structure.at_frac[ct_atom, i] = temp_vec[i]/N[i] new_structure.namelist.append(VASP_struct.POSCAR.namelist[ct2]) new_structure.atom_id.append(VASP_struct.POSCAR.atom_id[ct2]) ct_atom += 1 ct2 += 1 for i in range(3): new_structure.B[i] = np.copy(VASP_struct.POSCAR.B[i]*N[i]) new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac)) mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac)) new_structure.volume *= mult #new_structure.energy *= mult return new_structure def new_supercell(self, N, structure): # We select and divide into smaller functions as a sort of override if structure.type == 'VASP': new_structure = self.new_supercell_VASP(N, structure) return new_structure # Add atoms given a vector def spawn_cell_frac(self, vector, structure): new_frac = np.copy(structure.at_frac) for i in range(len(new_frac)): new_frac[i] = structure.at_frac[i] + np.array(vector, dtype=float) return np.copy(new_frac) def spawn_cell_invfrac(self, vector, structure): new_frac = np.copy(structure.at_frac) tol = 0.02 for i in range(len(new_frac)): """ temp_vec = -structure.at_frac[i] + np.array([1,1,1], dtype=float) # Correct frac on the fly for i in range(3): if(np.abs(temp_vec[i]-1.0) < tol): temp_vec[i] -= 0.0#1.0 #new_frac[i] = temp_vec + np.array(vector, dtype=float) """ # We go to [-1,-1,-1] and then invert! temp_vec = -(structure.at_frac[i] + np.array([-1,-1,-1], dtype=float)) # Correct frac on the fly for j in range(3): if(np.abs(temp_vec[j]-1.0) < tol): temp_vec[j] -= 1.0 new_frac[i] = temp_vec + np.array(vector, dtype=float) #new_frac[i] = np.array(vector, dtype=float)-structure.at_frac[i] + np.array([-1,-1,-1], dtype=float) return np.copy(new_frac) def centrosymmetric_supercell_VASP(self, VASP_structure): new_VASP = self.copy_structure(VASP_structure) new_structure = new_VASP.POSCAR # Centrosymmetry requires an atom well located for the origin. # Thus we need to shift to set the zero (for now experimental) shifted_structure = self.copy_structure(structure).POSCAR for i in range(len(shifted_structure.at_frac)): shifted_structure.at_frac[i] -= structure.at_frac[0] cells = [] """ for i, j in [(i,j) for i in range(2) for j in range(2)]: cells.append(self.spawn_cell_frac([i,j,0], shifted_structure)) for i, j in [(i,j) for i in range(2) for j in range(2)]: cells.append(self.spawn_cell_invfrac([i,j,1], shifted_structure)) """ # Test: only 2 cells cells.append(self.spawn_cell_frac([0,0,0], shifted_structure)) cells.append(self.spawn_cell_invfrac([0,0,1], shifted_structure)) N = [1,1,2] ct = 0 ct2 = 0 new_structure.at_frac = np.zeros([np.prod(N)*len(structure.at_frac), 3]) new_structure.namelist = [''] new_structure.atom_id = [] for specie_id, specie in enumerate(structure.names): new_structure.multiplicity[specie_id] = structure.multiplicity[specie_id]*np.prod(N) for atom in range(structure.multiplicity[specie_id]): for cell in cells: # Frac coordinates need to be reescaled to the new supercell temp_vec = np.copy(cell[ct2]) for i in range(3): new_structure.at_frac[ct, i] = temp_vec[i]/N[i] new_structure.atom_id.append(structure.atom_id[ct2]) new_structure.namelist.append(structure.namelist[ct2]) ct += 1 ct2 += 1 for i in range(3): new_structure.B[i] = np.copy(structure.B[i]*N[i]) new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac)) mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac)) new_structure.volume *= mult new_structure.energy *= mult return new_structure def centrosymmetric_supercell(self, structure): # We select and divide into smaller functions as a sort of override if structure.type == 'VASP': new_structure = self.new_supercell_VASP(N, structure) return new_structure def interpolate_structures_VASP(self, struct1, struct2, num_images): structures = [] structures.append(struct1) for i in range(1, num_images+1): # Load structure + cleanup of frac coordinates new_VASP = self.copy_structure(struct1) new_structure = new_VASP.POSCAR new_structure.frac_image_sniper() # Linear interpolation of cell & frac & cart new_structure.B = struct1.POSCAR.B + float(i)/(num_images+2)*(struct2.POSCAR.B-struct1.POSCAR.B) new_structure.at_frac = struct1.POSCAR.at_frac + float(i)/(num_images+2)*(struct2.POSCAR.at_frac-struct1.POSCAR.at_frac) # Could put a control for running images here new_structure.at_cart = self.frac_to_cart(new_structure.B, new_structure.at_frac)#struct1.at_cart + float(i/(num_images+2))*(struct2.at_cart-struct1.at_cart) # Probably useless or troublesome: new_structure.frac_image_sniper() structures.append(new_VASP) structures.append(struct2) return structures def interpolate_structures(self, struct1, struct2, num_images): # We select and divide into smaller functions as a sort of override if struct1.type != struct2.type: print('Error! Structures in different format. Transform them first!') sys.exit() # Attempt conversion #print('Attempting to unify types...') #struct1 = self.transform_type(struct1, struct2.type) #print("Successfully transformed type of structure 1 (%s) into %s!" % (struct1.type, struct2.type)) if struct1.type == 'VASP': structures = self.interpolate_structures_VASP(struct1, struct2, num_images) return structures @calculate_time def find_firstneighbors(self, atom, satellites, basis): d = np.zeros([len(satellites)], dtype=float) tol = 20 # in percentage for i in range(len(satellites)): best_transform, transform_key = self.findBestPairPeriodic(atom, satellites[i], basis) dist = np.sqrt(np.dot(atom-best_transform, atom-best_transform)) # We exclude the "self" atom if(dist > 0.0): d[i] = dist else: d[i] = 1E5 #print(np.sort(d)) min_dist = np.min(d) list_neighbors = [] for i in range(len(satellites)): if d[i] < min_dist*(1+tol/100): #print(i, d[i], min_dist*(1+tol/100)) list_neighbors.append(i) return list_neighbors def find_full_molecules(self, struct, center, satellite, num_neighbors=0): center_atoms = struct.filter_atoms(center) sat_atoms = struct.filter_atoms(satellite) all_atoms = np.copy(struct.at_cart) center_atoms_list = struct.filter_atoms_list(center) sat_atoms_list = struct.filter_atoms_list(satellite) groups = [] nice_molecules = [] for i in range(len(center_atoms)): nice_molecule = [] sat_atom_neighbors = [] list_neighbors = self.find_firstneighbors(center_atoms[i], sat_atoms, struct.B) for j in list_neighbors: sat_atom_neighbors.append(sat_atoms_list[j]) if num_neighbors==1: # center atom new first neighbors cfn_bulk = self.find_firstneighbors(center_atoms[i], all_atoms, struct.B) cfn_list = [] for cfn in cfn_bulk: if ((cfn not in center_atoms_list) and (cfn not in sat_atom_neighbors)): cfn_list.append(cfn) # find the first neighbors of every satellite sfn_list_col = [] for j in sat_atom_neighbors: sfn_bulk = self.find_firstneighbors(struct.at_cart[j], all_atoms, struct.B) sfn_list = [] for sfn in sfn_bulk: if ((sfn not in center_atoms_list) and (sfn not in sat_atom_neighbors) and (sfn not in cfn_list)): sfn_list.append(sfn) sfn_list_col.append(sfn_list) groups.append([center_atoms_list[i], sat_atom_neighbors, cfn_list, sfn_list_col]) nice_molecule.append(center_atoms_list[i]) for sat in sat_atom_neighbors: nice_molecule.append(sat) for cfn in cfn_list: nice_molecule.append(cfn) for sfn in sfn_list: nice_molecule.append(sfn) nice_molecules.append(nice_molecule) else: groups.append([center_atoms_list[i], sat_atom_neighbors]) sys.stdout.write("%3.3f%%\r" % (100*(i+1)/len(center_atoms))) sys.stdout.flush() return groups, nice_molecules # Find groups of atoms between the CENTER # of the interaction and the SATELLITE possible # atoms belonging to a particular species @calculate_time def find_groups(self, struct, center, satellite, excludeSatList=[]): center_atoms = struct.filter_atoms(center) sat_atoms = struct.filter_atoms(satellite, excludeSatList=excludeSatList) center_atoms_list = struct.filter_atoms_list(center) sat_atoms_list = struct.filter_atoms_list(satellite, excludeSatList=excludeSatList) #print(len(sat_atoms), len(sat_atoms_list)) groups = [] for i in range(len(center_atoms)): sat_atom_neighbors = [] list_neighbors = self.find_firstneighbors(center_atoms[i], sat_atoms, struct.B) for j in list_neighbors: sat_atom_neighbors.append(sat_atoms_list[j]) groups.append([center_atoms_list[i], sat_atom_neighbors]) #sys.stdout.write("%3.3f%%\r" % (100*(i+1)/len(center_atoms))) #sys.stdout.flush() return groups # Sometimes atoms go out of the simulation box. Bound box. Whatever. # And sometimes we want them out to understand the structure. # So I add here a wrapper for: # 1) Application of periodic boundary conditions. # 2) Unbound atoms. # The code here works in fractional coordinates. # You can provide a structure, or a set of atoms, # or a cartesian set of atoms + basis... # Or at least, it will do that. For now, at_frac. @calculate_time def handle_periodic_boundaries(self, at_frac, mode='bound'): if mode != 'bound' or mode != 'unbound': print("PBC handler: using mode 'bound' by default (input not recognized)") mode = 'bound' #CONTINUE_HERE # This function searches for a periodic image of periodic_atom # that is the nearest to fixed_atom. Returns the best periodic # image found, and the key [k_x, k_y, k_z] necessary to transform it. @calculate_time def findBestPairPeriodic_test(self, fixed_atom, periodic_atom, basis): mindist = 1E3 best_transform = np.copy(periodic_atom) best_transform_key = np.array([0,0,0], dtype=float) # We search 1 cell around the boundary # Maybe we can search in a decomposed way i>j>k????? for axis in range(3): transform_key = np.copy(best_transform_key) for i in range(-1, 2): transform_key[axis] = i transformed_atom = periodic_atom + basis[0]*transform_key[0] + basis[1]*transform_key[1] + basis[2]*transform_key[2] vec = fixed_atom-transformed_atom dist = np.sqrt(np.dot(vec, vec)) if (dist-mindist) < 0.0: mindist = dist + 0.0 best_transform = np.copy(transformed_atom) best_transform_key[axis] = i return best_transform, best_transform_key @calculate_time def findBestPairPeriodic_GPT(self, fixed_atom, periodic_atom, basis): translations = np.array([i*basis[0] + j*basis[1] + k*basis[2] for i in range(-1,2) for j in range(-1,2) for k in range(-1,2)]) #transformed_atoms = periodic_atom + translations #print(translations) transformed_atoms = np.array([periodic_atom + translations[i] for i in range(len(translations))]) #print(transformed_atoms) #vec = fixed_atom - transformed_atoms vec = np.array([fixed_atom + transformed_atoms[i] for i in range(len(transformed_atoms))]) dist = np.linalg.norm(vec, axis=1) min_index = np.argmin(dist) #print(np.min(dist), min_index) return transformed_atoms[min_index], [min_index//9-1, (min_index//3)%3-1, min_index%3-1] # This function searches for a periodic image of periodic_atom # that is the nearest to fixed_atom. Returns the best periodic # image found, and the key [k_x, k_y, k_z] necessary to transform it. @calculate_time def findBestPairPeriodic(self, fixed_atom, periodic_atom, basis): mindist = 1E3 best_transform = np.copy(periodic_atom) transform_key = [0,0,0] # We search 1 cell around the boundar """ bi, bj, bk = 0, 0, 0 for i in range(-1, 2): transformed_atom = periodic_atom + basis[0]*float(i) + basis[1]*float(bj) + basis[2]*float(bk) vec = fixed_atom-transformed_atom dist = np.sqrt(np.dot(vec, vec)) try: if (dist-mindist) < 0.0: mindist = dist best_transform = np.copy(transformed_atom) bi = i except: print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec) sys.exit() for j in range(-1, 2): transformed_atom = periodic_atom + basis[0]*float(bi) + basis[1]*float(j) + basis[2]*float(bk) vec = fixed_atom-transformed_atom dist = np.sqrt(np.dot(vec, vec)) try: if (dist-mindist) < 0.0: mindist = dist best_transform = np.copy(transformed_atom) bj = j except: print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec) sys.exit() for k in range(-1, 2): transformed_atom = periodic_atom + basis[0]*float(bi) + basis[1]*float(bj) + basis[2]*float(k) vec = fixed_atom-transformed_atom dist = np.sqrt(np.dot(vec, vec)) try: if (dist-mindist) < 0.0: mindist = dist best_transform = np.copy(transformed_atom) bk = k except: print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec) sys.exit() transform_key = [bi, bj, bk] """ for i in range(-1, 2): for j in range(-1, 2): for k in range(-1, 2): transformed_atom = periodic_atom + basis[0]*float(i) + basis[1]*float(j) + basis[2]*float(k) vec = fixed_atom-transformed_atom dist = np.sqrt(np.dot(vec, vec)) #print(transformed_atom, dist, mindist, best_transform) try: if (dist-mindist) < 0.0: mindist = dist best_transform = np.copy(transformed_atom) transform_key = [i, j, k] except: print("IT BROKE! findBestPeriodic doesn't work well!", dist, mindist, vec) sys.exit() return best_transform, transform_key @calculate_time def atom_transform(self, atom, basis, key): return atom + basis[0]*float(key[0]) + basis[1]*float(key[1]) + basis[2]*float(key[2]) # Function to find pairs of atoms. Input can be in standard .at_frac # Returns the pair[atom1]=atom2 and the keyring [k_x, k_y, k_z] for the periodic images. @calculate_time def find_pairs(self, atoms1, atoms2, box2): pair = np.zeros([len(atoms1)], dtype=int) pair[:] -= 1 mindist_pair = np.zeros([len(atoms1)], dtype=float) mindist_pair[:] -= 1 keyring = [] listat2 = list(range(len(atoms2))) for i in range(len(atoms1)): threshold = 1E3 # This is just an init parameter for j in listat2: atoms2periodic, transform_key = self.findBestPairPeriodic_test(atoms1[i], atoms2[j], box2) dvec = np.copy(atoms1[i]-atoms2periodic) dist = np.sqrt(np.dot(dvec, dvec)) # This is unclean but efficient. Basically, checks that the new transformation # provides a smaller distance between pairs of atoms. Probably can be cleaned. if (dist-threshold) < 0.0: threshold = dist + 0.0 pair[i] = j best_transform_key = transform_key if pair[i] in listat2: listat2.remove(pair[i]) keyring.append(best_transform_key) mindist_pair[i] = threshold + 0.0 for i, a2 in enumerate(pair): # This warning should be set to an actual threshold parameter if mindist_pair[i] > 2.5 and a2 > -1: print("WARNING with pair %d %d min distance: %.5f" % (i, a2, mindist_pair[i])) #print("Pair %d %d: %.5f" % (i, a2, mindist_pair[i]), atoms1[i], atoms2[a2], keyring[i]) # DEBUG return pair, keyring # A quicker function to find keys of known pairs of atoms @calculate_time def refresh_key(self, atoms1, atoms2, box2, pairs): key = [] for at1, at2 in enumerate(pairs): atoms2periodic, transform_key = self.findBestPairPeriodic_test(atoms1[at1], atoms2[at2], box2) key.append(transform_key) return key # Key is for the atom 2. @calculate_time def build_vectormap(self, atoms1, atoms2, box, pairs, initial_key): vectors = np.zeros([len(atoms1), 3], dtype=float) key = initial_key + .0 d = [] for i, j in enumerate(pairs): atoms2_pos_correction = key[i][0]*box[0] + key[i][1]*box[1] + key[i][2]*box[2] vec = atoms1[i] - (atoms2[j] + atoms2_pos_correction) vectors[i] = np.copy(vec) d.append(np.sqrt(np.dot(vec,vec))) #print(d[-1]) # The tolerance should probably be a parameter if (np.max(np.array(d, dtype=float)) > 2.5): key = self.refresh_key(atoms1, atoms2, box, pairs) d = [] for i, j in enumerate(pairs): atoms2_pos_correction = key[i][0]*box[0] + key[i][1]*box[1] + key[i][2]*box[2] vec = atoms1[i] - (atoms2[j] + atoms2_pos_correction) vectors[i] = np.copy(vec) dist = np.sqrt(np.dot(vec, vec)) d.append(dist) # Just a check if (np.max(np.array(d, dtype=float)) > 2.5): print("Max distance between atoms is larger than 2.5") sys.exit() return vectors, key def compute_angles_MD_GPT(self, vectors, vec_of_vecs=False): num_steps, num_vecs = vectors.shape[:2] e_x = np.array([1, 0, 0], dtype=np.float32) e_z = np.array([0, 0, 1], dtype=np.float32) v_pbc = vectors - np.einsum('ij,j->ij', vectors, e_x) * e_x v_pab = vectors - np.einsum('ij,j->ij', vectors, e_z) * e_z norm_vpbc = np.linalg.norm(v_pbc, axis=-1) norm_vpab = np.linalg.norm(v_pab, axis=-1) phi = np.arctan2(np.cross(v_pab, e_z), np.dot(v_pab, e_x)) theta = np.arccos(np.einsum('ij,j->i', vectors, e_z) / norm_vpbc) ctheta = np.cos(theta) if vec_of_vecs: angles = np.concatenate((phi.reshape(-1,1), theta.reshape(-1,1)), axis=1) return angles.reshape(num_steps, num_vecs, 2) else: return phi, theta, ctheta @calculate_time def compute_angles_MD(self, vectors, vec_of_vecs=False): num_steps = len(vectors) num_vecs = len(vectors[0]) theta = np.zeros([num_steps*num_vecs], dtype=float) phi = np.zeros([num_steps*num_vecs], dtype=float) ctheta = np.zeros([num_steps*num_vecs], dtype=float) e_x = np.eye(3, dtype=float)[0,:] e_z = np.eye(3, dtype=float)[2,:] angles = [] for i in range(num_steps): angles_inner = [] for j in range(num_vecs): vec = np.copy(vectors[i,j]) v_pbc = vec - np.dot(vec, e_x)*e_x v_pab = vec - np.dot(vec, e_z)*e_z norm_vpbc = np.sqrt(np.dot(v_pbc, v_pbc)) norm_vpab = np.sqrt(np.dot(v_pab, v_pab)) # Angles = [\phi, \theta] phi[i*num_vecs+j] = self.calculate_angle_full(e_x, v_pab/norm_vpab, e_z) + 0.0 theta[i*num_vecs+j] = self.calculate_angle(e_z, vec)#angle(e_z, v_pbc/norm_vpbc, e_x) + 0.0 ctheta[i*num_vecs+j] = np.cos(theta[i*num_vecs+j]) angles_inner.append([phi[i*num_vecs+j], theta[i*num_vecs+j]]) angles.append(angles_inner) angles = np.array(angles) if vec_of_vecs == False: return phi, theta, ctheta else: return angles
Methods
def apply_rotation(self, vector, angle, axis, origin=None)
-
Expand source code
def apply_rotation(self, vector, angle, axis, origin=None): # The idea: # We want to rotate a group of atoms. The positions # of the atoms are given as vectors from the crystallographic # origin of coordinates. Thus what we want to do is: # move origin -> apply rotation along given axis -> go back to crystal. origin # We use the tensorial form of the rotation operator axis = np.array(axis) axis = axis/(np.sqrt(np.dot(axis, axis))) cross = np.zeros([3,3], dtype=float) cart = np.array([[1.0, .0, .0], [.0, 1.0, .0], [.0, .0, 1.0]], dtype=float) for i in range(3): c1 = np.cross(axis, cart[i]) cross += np.outer(c1, cart[i]) R = np.cos(angle)*np.eye(3,3) + (np.sin(angle))*(cross) + (1-np.cos(angle))*(np.outer(axis, axis)) # Now we move the origin, compute, and go back to the crystal. origin. All packed. if origin is None: origin = np.zeros([3], dtype=float) return np.dot(R, vector - origin) + origin
def atom_transform(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def build_vectormap(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def cart_to_frac(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def centrosymmetric_supercell(self, structure)
-
Expand source code
def centrosymmetric_supercell(self, structure): # We select and divide into smaller functions as a sort of override if structure.type == 'VASP': new_structure = self.new_supercell_VASP(N, structure) return new_structure
def centrosymmetric_supercell_VASP(self, VASP_structure)
-
Expand source code
def centrosymmetric_supercell_VASP(self, VASP_structure): new_VASP = self.copy_structure(VASP_structure) new_structure = new_VASP.POSCAR # Centrosymmetry requires an atom well located for the origin. # Thus we need to shift to set the zero (for now experimental) shifted_structure = self.copy_structure(structure).POSCAR for i in range(len(shifted_structure.at_frac)): shifted_structure.at_frac[i] -= structure.at_frac[0] cells = [] """ for i, j in [(i,j) for i in range(2) for j in range(2)]: cells.append(self.spawn_cell_frac([i,j,0], shifted_structure)) for i, j in [(i,j) for i in range(2) for j in range(2)]: cells.append(self.spawn_cell_invfrac([i,j,1], shifted_structure)) """ # Test: only 2 cells cells.append(self.spawn_cell_frac([0,0,0], shifted_structure)) cells.append(self.spawn_cell_invfrac([0,0,1], shifted_structure)) N = [1,1,2] ct = 0 ct2 = 0 new_structure.at_frac = np.zeros([np.prod(N)*len(structure.at_frac), 3]) new_structure.namelist = [''] new_structure.atom_id = [] for specie_id, specie in enumerate(structure.names): new_structure.multiplicity[specie_id] = structure.multiplicity[specie_id]*np.prod(N) for atom in range(structure.multiplicity[specie_id]): for cell in cells: # Frac coordinates need to be reescaled to the new supercell temp_vec = np.copy(cell[ct2]) for i in range(3): new_structure.at_frac[ct, i] = temp_vec[i]/N[i] new_structure.atom_id.append(structure.atom_id[ct2]) new_structure.namelist.append(structure.namelist[ct2]) ct += 1 ct2 += 1 for i in range(3): new_structure.B[i] = np.copy(structure.B[i]*N[i]) new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac)) mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac)) new_structure.volume *= mult new_structure.energy *= mult return new_structure
def compute_angles_MD(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def compute_angles_MD_GPT(self, vectors, vec_of_vecs=False)
-
Expand source code
def compute_angles_MD_GPT(self, vectors, vec_of_vecs=False): num_steps, num_vecs = vectors.shape[:2] e_x = np.array([1, 0, 0], dtype=np.float32) e_z = np.array([0, 0, 1], dtype=np.float32) v_pbc = vectors - np.einsum('ij,j->ij', vectors, e_x) * e_x v_pab = vectors - np.einsum('ij,j->ij', vectors, e_z) * e_z norm_vpbc = np.linalg.norm(v_pbc, axis=-1) norm_vpab = np.linalg.norm(v_pab, axis=-1) phi = np.arctan2(np.cross(v_pab, e_z), np.dot(v_pab, e_x)) theta = np.arccos(np.einsum('ij,j->i', vectors, e_z) / norm_vpbc) ctheta = np.cos(theta) if vec_of_vecs: angles = np.concatenate((phi.reshape(-1,1), theta.reshape(-1,1)), axis=1) return angles.reshape(num_steps, num_vecs, 2) else: return phi, theta, ctheta
def findBestPairPeriodic(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def findBestPairPeriodic_GPT(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def findBestPairPeriodic_test(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def find_firstneighbors(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def find_full_molecules(self, struct, center, satellite, num_neighbors=0)
-
Expand source code
def find_full_molecules(self, struct, center, satellite, num_neighbors=0): center_atoms = struct.filter_atoms(center) sat_atoms = struct.filter_atoms(satellite) all_atoms = np.copy(struct.at_cart) center_atoms_list = struct.filter_atoms_list(center) sat_atoms_list = struct.filter_atoms_list(satellite) groups = [] nice_molecules = [] for i in range(len(center_atoms)): nice_molecule = [] sat_atom_neighbors = [] list_neighbors = self.find_firstneighbors(center_atoms[i], sat_atoms, struct.B) for j in list_neighbors: sat_atom_neighbors.append(sat_atoms_list[j]) if num_neighbors==1: # center atom new first neighbors cfn_bulk = self.find_firstneighbors(center_atoms[i], all_atoms, struct.B) cfn_list = [] for cfn in cfn_bulk: if ((cfn not in center_atoms_list) and (cfn not in sat_atom_neighbors)): cfn_list.append(cfn) # find the first neighbors of every satellite sfn_list_col = [] for j in sat_atom_neighbors: sfn_bulk = self.find_firstneighbors(struct.at_cart[j], all_atoms, struct.B) sfn_list = [] for sfn in sfn_bulk: if ((sfn not in center_atoms_list) and (sfn not in sat_atom_neighbors) and (sfn not in cfn_list)): sfn_list.append(sfn) sfn_list_col.append(sfn_list) groups.append([center_atoms_list[i], sat_atom_neighbors, cfn_list, sfn_list_col]) nice_molecule.append(center_atoms_list[i]) for sat in sat_atom_neighbors: nice_molecule.append(sat) for cfn in cfn_list: nice_molecule.append(cfn) for sfn in sfn_list: nice_molecule.append(sfn) nice_molecules.append(nice_molecule) else: groups.append([center_atoms_list[i], sat_atom_neighbors]) sys.stdout.write("%3.3f%%\r" % (100*(i+1)/len(center_atoms))) sys.stdout.flush() return groups, nice_molecules
def find_groups(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def find_pairs(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def frac_to_cart(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def func3(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def handle_periodic_boundaries(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def interpolate_structures(self, struct1, struct2, num_images)
-
Expand source code
def interpolate_structures(self, struct1, struct2, num_images): # We select and divide into smaller functions as a sort of override if struct1.type != struct2.type: print('Error! Structures in different format. Transform them first!') sys.exit() # Attempt conversion #print('Attempting to unify types...') #struct1 = self.transform_type(struct1, struct2.type) #print("Successfully transformed type of structure 1 (%s) into %s!" % (struct1.type, struct2.type)) if struct1.type == 'VASP': structures = self.interpolate_structures_VASP(struct1, struct2, num_images) return structures
def interpolate_structures_VASP(self, struct1, struct2, num_images)
-
Expand source code
def interpolate_structures_VASP(self, struct1, struct2, num_images): structures = [] structures.append(struct1) for i in range(1, num_images+1): # Load structure + cleanup of frac coordinates new_VASP = self.copy_structure(struct1) new_structure = new_VASP.POSCAR new_structure.frac_image_sniper() # Linear interpolation of cell & frac & cart new_structure.B = struct1.POSCAR.B + float(i)/(num_images+2)*(struct2.POSCAR.B-struct1.POSCAR.B) new_structure.at_frac = struct1.POSCAR.at_frac + float(i)/(num_images+2)*(struct2.POSCAR.at_frac-struct1.POSCAR.at_frac) # Could put a control for running images here new_structure.at_cart = self.frac_to_cart(new_structure.B, new_structure.at_frac)#struct1.at_cart + float(i/(num_images+2))*(struct2.at_cart-struct1.at_cart) # Probably useless or troublesome: new_structure.frac_image_sniper() structures.append(new_VASP) structures.append(struct2) return structures
def new_supercell(self, N, structure)
-
Expand source code
def new_supercell(self, N, structure): # We select and divide into smaller functions as a sort of override if structure.type == 'VASP': new_structure = self.new_supercell_VASP(N, structure) return new_structure
def new_supercell_VASP(self, N, VASP_struct)
-
Expand source code
def new_supercell_VASP(self, N, VASP_struct): new_VASP = self.copy_structure(VASP_struct) # Objects can be different... new_structure = new_VASP.POSCAR new_structure.at_frac = np.zeros([np.prod(N)*len(VASP_struct.POSCAR.at_frac), 3]) ct_atom = 0 ct2 = 0 new_structure.namelist = [''] new_structure.atom_id = [] for specie_id, specie in enumerate(VASP_struct.POSCAR.names): new_structure.multiplicity[specie_id] = VASP_struct.POSCAR.multiplicity[specie_id]*np.prod(N) for atom in range(VASP_struct.POSCAR.multiplicity[specie_id]): #for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]: #for atom in range(structure.multiplicity[specie_id]): for nx, ny, nz in [(nx, ny, nz) for nx in range(N[0]) for ny in range(N[1]) for nz in range(N[2])]: #This inverts how the atoms are printed Id = np.eye(3) cell_vector = nx*Id[0] + ny*Id[1] + nz*Id[2] # Frac coordinates need to be reescaled to the new supercell temp_vec = np.copy(VASP_struct.POSCAR.at_frac[ct2] + cell_vector) for i in range(3): new_structure.at_frac[ct_atom, i] = temp_vec[i]/N[i] new_structure.namelist.append(VASP_struct.POSCAR.namelist[ct2]) new_structure.atom_id.append(VASP_struct.POSCAR.atom_id[ct2]) ct_atom += 1 ct2 += 1 for i in range(3): new_structure.B[i] = np.copy(VASP_struct.POSCAR.B[i]*N[i]) new_structure.at_cart = np.copy(self.frac_to_cart(new_structure.B, new_structure.at_frac)) mult = float(len(VASP_struct.POSCAR.at_frac))/float(len(new_structure.at_frac)) new_structure.volume *= mult #new_structure.energy *= mult return new_structure
def refresh_key(*args, **kwargs)
-
Expand source code
def inner1(*args, **kwargs): # storing time before function execution begin = time.time() val = func(*args, **kwargs) # storing time after function execution end = time.time() timer_name = func.__name__ timer_time = end-begin if timer_name in timers_dict: timers_dict[timer_name] += timer_time else: timers_dict[timer_name] = timer_time return val
def rigid_displacement(self, vector, structure)
-
Expand source code
def rigid_displacement(self, vector, structure): new_cart = np.zeros([len(structure.at_cart), 3], dtype=float) new_frac = np.zeros([len(structure.at_frac), 3], dtype=float) vector = np.array(vector, dtype=float) disp = np.zeros([3], dtype=float) for i in range(3): disp += vector[i]*structure.B[i,:] for atom in range(len(structure.at_cart)): new_cart[atom] = np.copy(structure.at_cart[atom] + disp) structure.at_cart = np.copy(new_cart) structure.recalculate_poscar()
def rigid_rotation(self, vector_list, angle, axis, origin=None)
-
Expand source code
def rigid_rotation(self, vector_list, angle, axis, origin=None): if len(vector_list.shape) < 2: return self.apply_rotation(vector_list, angle, axis, origin) else: veclist = [] for i in range(len(vector_list)): veclist.append(self.apply_rotation(vector_list[i], angle, axis, origin)) return np.array(veclist, dtype=float)
def spawn_cell_frac(self, vector, structure)
-
Expand source code
def spawn_cell_frac(self, vector, structure): new_frac = np.copy(structure.at_frac) for i in range(len(new_frac)): new_frac[i] = structure.at_frac[i] + np.array(vector, dtype=float) return np.copy(new_frac)
def spawn_cell_invfrac(self, vector, structure)
-
Expand source code
def spawn_cell_invfrac(self, vector, structure): new_frac = np.copy(structure.at_frac) tol = 0.02 for i in range(len(new_frac)): """ temp_vec = -structure.at_frac[i] + np.array([1,1,1], dtype=float) # Correct frac on the fly for i in range(3): if(np.abs(temp_vec[i]-1.0) < tol): temp_vec[i] -= 0.0#1.0 #new_frac[i] = temp_vec + np.array(vector, dtype=float) """ # We go to [-1,-1,-1] and then invert! temp_vec = -(structure.at_frac[i] + np.array([-1,-1,-1], dtype=float)) # Correct frac on the fly for j in range(3): if(np.abs(temp_vec[j]-1.0) < tol): temp_vec[j] -= 1.0 new_frac[i] = temp_vec + np.array(vector, dtype=float) #new_frac[i] = np.array(vector, dtype=float)-structure.at_frac[i] + np.array([-1,-1,-1], dtype=float) return np.copy(new_frac)