def extend_structure(structure, shell_r, pure_substrate=True, remove_dict=None): if pure_substrate is False: if remove_dict is None: print('ERROR: remove_list can not be None when substrate includes doped atoms.\n') exit(1) structure = remove_doped_atoms(structure, remove_dict) cell = structure.cell.T volume = structure.volume hz = volume/np.linalg.norm(np.cross(cell[0], cell[1])) hx = volume/np.linalg.norm(np.cross(cell[1], cell[2])) hy = volume/np.linalg.norm(np.cross(cell[2], cell[0])) if min(hx, hy, hz)/2 >= shell_r: return structure nz = int(np.ceil(shell_r/hz - 0.5)) nx = int(np.ceil(shell_r/hx - 0.5)) ny = int(np.ceil(shell_r/hy - 0.5)) temp = [] for i in range(-nx, nx+1): for j in range(-ny, ny+1): for k in range(-nz, nz+1): for atom in structure: position = atom.pos + i*cell[0] + j*cell[1] + k*cell[2] temp.append(Atom(position, atom.type)) supercell = Structure(structure.cell, scale=structure.scale) supercell.extend(temp) return supercell
def supercell(structure, transform): from method.structure import Structure import numpy as np from numpy import dot r = [] for j in range(3): minimum = 0 maximum = 0 for i in range(3): if transform[i][j] <= 0: minimum += transform[i][j] else: maximum += transform[i][j] r.append([minimum, maximum]) result = Structure(dot(structure.cell, transform.T), scale=structure.scale) inverse = np.linalg.inv(transform) inv_cell = np.linalg.inv(structure.cell) for i in range(len(structure)): fractional = dot(inv_cell, structure[i].pos) for a0 in range(r[0][0]-1, r[0][1]+1): for a1 in range(r[1][0]-1, r[1][1]+1): for a2 in range(r[2][0]-1, r[2][1]+1): translation = np.array([a0, a1, a2]) temp = dot((translation + fractional), inverse) if is_in_cell(temp): result.add_atom(dot(result.cell, temp), structure[i].type) return result
def remove_doped_atoms(structure, remove_dict): temp = [] for atom in structure: if atom.type in remove_dict.keys(): value = remove_dict[atom.type] if value != '': temp.append(Atom(atom.pos, value)) else: temp.append(Atom(atom.pos, atom.type)) new_structure = Structure(structure.cell, scale=structure.scale) new_structure.extend(temp) return new_structure
def build_shell(structure, pure_substrate=False, doped_atom=None, remove_dict=None, cluster_r=5, shell_r=30): """ :param structure: Pylada.crystal.Structure :param pure_substrate: Bool type :param doped_atom: Pylada.crystal.Atom :param remove_dict: dict :param cluster_r: float :param shell_r: float :return: """ if pure_substrate is True: pass else: if doped_atom is None: species = structure_species(structure) doped_name = species[0][0] num = species[0][1] if num != 1: print( 'WARNING: The number of doped atom is not exclusive, please give doped atom name and position.\n' ) for atom in structure: if atom.type == doped_name: doped_atom = Atom(atom.pos, atom.type) break if pure_substrate is False: if remove_dict is None: print( 'ERROR: remove_list can not be None when substrate includes doped atoms.\n' ) exit(1) structure = remove_doped_atoms(structure, remove_dict) supercell = extend_structure(structure, shell_r) shell = Structure(structure.cell, scale=structure.scale) doped_center = doped_atom.pos temp = [] for atom in supercell: position = atom.pos d = np.linalg.norm(position - doped_center) if cluster_r < d <= shell_r: temp.append(Atom(position - doped_center, atom.type)) shell.extend(temp) return shell
def extend_structure(structure, shell_r=30): cell = structure.cell.T volume = structure.volume hz = volume / np.linalg.norm(np.cross(cell[0], cell[1])) hx = volume / np.linalg.norm(np.cross(cell[1], cell[2])) hy = volume / np.linalg.norm(np.cross(cell[2], cell[0])) if min(hx, hy, hz) >= shell_r: return structure nz = int(np.ceil(shell_r / hz - 0.5)) nx = int(np.ceil(shell_r / hx - 0.5)) ny = int(np.ceil(shell_r / hy - 0.5)) temp = [] for i in range(-nx, nx + 1): for j in range(-ny, ny + 1): for k in range(-nz, nz + 1): for atom in structure: position = atom.pos + i * cell[0] + j * cell[1] + k * cell[ 2] temp.append(Atom(position, atom.type)) supercell = Structure(structure.cell, scale=structure.scale) supercell.extend(temp) return supercell
def build_shell(structure, center, cluster_r, shell_r, pure_substrate=False, remove_dict=None): if pure_substrate is False: if remove_dict is None: print( 'ERROR: remove_list can not be None when substrate includes doped atoms.\n' ) exit(1) structure = remove_doped_atoms(structure, remove_dict) supercell = extend_structure(structure, shell_r) shell = Structure(structure.cell, scale=structure.scale) temp = [] for atom in supercell: position = atom.pos d = np.linalg.norm(position - center) if cluster_r < d <= shell_r: temp.append(Atom(position - center, atom.type)) shell.extend(temp) return shell
def supercell(structure, transform): from method.structure import Structure from method.atom import Atom import numpy as np from numpy import dot epsilon = 1e-3 r = [] surface = [] for j in range(3): minimum = 0 maximum = 0 for i in range(3): if transform[i][j] <= 0: minimum += transform[i][j] else: maximum += transform[i][j] r.append([minimum, maximum]) result = Structure(dot(structure.cell, transform.T), scale=structure.scale) inverse = np.linalg.inv(transform) inv_cell = np.linalg.inv(structure.cell) for i in range(len(structure)): fractional = dot(inv_cell, structure[i].pos) for a0 in range(r[0][0] - 1, r[0][1] + 1): for a1 in range(r[1][0] - 1, r[1][1] + 1): for a2 in range(r[2][0] - 1, r[2][1] + 1): translation = np.array([a0, a1, a2]) temp = dot((translation + fractional), inverse) if is_in_cell(temp, epsilon): result.add_atom(dot(result.cell, temp), structure[i].type) elif is_in_surface(temp, epsilon): surface.append( Atom(dot(result.cell, temp), structure[i].type, flag=0)) surface = remove_reduplicate(surface, dot(structure.cell, transform.T), epsilon) result.extend(surface) return result
def poscar(path="POSCAR", types=None): """ Tries to read a VASP POSCAR file. :param path: Path to the POSCAR file. Can also be an object with file-like behavior. :type path: str or file object :param types: Species in the POSCAR. :type types: None or sequence of str :return: `pylada.crystal.Structure` instance. """ import re from os.path import join, exists, isdir from copy import deepcopy from numpy import array, dot, transpose from numpy.linalg import det from quantities import angstrom from method.structure import Structure from method import error # if types is not none, converts to a list of strings. if types is not None: if isinstance(types, str): types = [types] # can't see another way of doing this... elif not hasattr(types, "__iter__"): types = [str(types)] # single lone vasp.specie.Specie else: types = [str(s) for s in types] if path is None: path = "POSCAR" if not hasattr(path, 'read'): assert exists(path), IOError("Could not find path %s." % (path)) if isdir(path): assert exists(join(path, "POSCAR")), IOError("Could not find POSCAR in %s." % (path)) path = join(path, "POSCAR") result = Structure() poscar = path if hasattr(path, "read") else open(path, 'r') try: # gets name of structure result.name = poscar.readline().strip() if len(result.name) > 0 and result.name[0] == "#": result.name = result.name[1:].strip() # reads scale scale = float(poscar.readline().split()[0]) # gets cell vectors. cell = [] for i in range(3): line = poscar.readline() assert len(line.split()) >= 3,\ RuntimeError("Could not read column vector from poscar: %s." % (line)) cell.append([float(f) for f in line.split()[:3]]) result.cell = transpose(array(cell)) vol = det(cell) if scale < 1.E-8: scale = abs(scale / vol) ** (1.0 / 3) result.scale = scale * angstrom # checks for vasp 5 input. is_vasp_5 = True line = poscar.readline().split() for i in line: if not re.match(r"[A-Z][a-z]?", i): is_vasp_5 = False break if is_vasp_5: text_types = deepcopy(line) if types is not None and not set(text_types).issubset(set(types)): raise error.ValueError("Unknown species in poscar: {0} not in {1}." .format(text_types, types)) types = text_types line = poscar.readline().split() if types is None: raise RuntimeError("No atomic species given in POSCAR or input.") # checks/reads for number of each specie if len(types) < len(line): raise RuntimeError("Too many atomic species in POSCAR.") nb_atoms = [int(u) for u in line] # Check whether selective dynamics, cartesian, or direct. first_char = poscar.readline().strip().lower()[0] selective_dynamics = False if first_char == 's': selective_dynamics = True first_char = poscar.readline().strip().lower()[0] # Checks whether cartesian or direct. is_direct = first_char not in ['c', 'k'] # reads atoms. for n, specie in zip(nb_atoms, types): for i in range(n): line = poscar.readline().split() pos = array([float(u) for u in line[:3]], dtype="float64") if is_direct: pos = dot(result.cell, pos) result.add_atom(pos=pos, type=specie) if selective_dynamics: for which, freeze in zip(line[3:], ['x', 'y', 'z']): if which.lower()[0] == 't': result[-1].freeze = getattr(result[-1], 'freeze', '') + freeze finally: poscar.close() return result
def build_cluster(structure, doped_atom=None, cluster_r=5, core_r=None, tolerance=0.2): from coordination_shells import coordination_shells """ :param structure: Pylada.crystal.Structure :param doped_atom: Pylada.crystal.Atom :param cluster_r: float :param core_r: float :return: cluster, Pylada.crystal.Structure """ if doped_atom is None: species = structure_species(structure) doped_name = species[0][0] num = species[0][1] if num != 1: print( 'WARNING: The number of doped atom is not exclusive, please give doped atom name and position.\n' ) for atom in structure: if atom.type == doped_name: doped_atom = Atom(atom.pos, atom.type) break cluster = Structure(structure.cell, scale=structure.scale) doped_center = doped_atom.pos if core_r is not None: for atom in structure: position = atom.pos d = np.linalg.norm(doped_center - position) if d <= core_r: cluster.append( Atom(atom.pos - doped_center, atom.type, pseudo=0)) elif d <= cluster_r: cluster.append( Atom(atom.pos - doped_center, atom.type, pseudo=-1)) else: # try to find the nearest neighbors of doped atom neighbors = coordination_shells(structure, nshells=4, center=doped_center, tolerance=tolerance) nearest_neighbors = [] flag = 0 for item in neighbors[0]: atom = item[0] nearest_neighbors.append(atom) atom.pseudo = 0 if len(sorted({a.type for a in nearest_neighbors})) > 1: print( 'WARNING: The species of nearest neighbor atoms are more than one.\n' ) atom = neighbors[0][0][0] nearest_name = atom.type for i in range(1, 4): if flag != 0: break temp = [item[0] for item in neighbors[i]] if len(sorted({a.type for a in temp})) > 1: print('WARNING: The species of next nearest neighbor atoms:\n') print(sorted({a.type for a in temp})) print( '\nwhich means the tolerance parameter might be too big to distinguish different shell of ' 'neighbors\n') # when this situation happened, we discard this shell. break for item in neighbors[i]: atom = item[0] if nearest_name == atom.type: nearest_neighbors.append(atom) atom.pseudo = 0 else: flag = 1 for atom in structure: position = atom.pos d = np.linalg.norm(doped_center - position) if d <= cluster_r: if hasattr(atom, 'pseudo'): cluster.append( Atom(atom.pos - doped_center, atom.type, pseudo=0)) elif atom.type == doped_atom.type and d < 0.01: cluster.append( Atom(atom.pos - doped_center, atom.type, pseudo=0)) else: cluster.append( Atom(atom.pos - doped_center, atom.type, pseudo=-1)) return cluster
def build_cluster(structure, cluster_r, doped_atom_type=None, center=None, core_r=None, tolerance=0.2): from method.coordination_shells import coordination_shells position = np.array([0.0, 0.0, 0.0]) species = structure_species(structure) if doped_atom_type is None: doped_atom_type = species[0][0] print('\nTry to use *{0}* atoms as doped atoms\n'.format( doped_atom_type)) if center is None: num = 0 for atom in structure: if atom.type == doped_atom_type: position += atom.pos num += 1 center = position / num if cluster_r > min(distance_of_center_and_surface(center, structure.cell.T)): print( '\nERROR: the cluster is out range of the CONTCAR supercell structure. ' 'Please use a bigger supercell or a smaller cluster_r parameter\n') exit(1) cluster = Structure(structure.cell, scale=structure.scale) if core_r is None: print( '\nTry to use the nearest neighbor of doped atoms to build core structure.\n' ) nearest_neighbors = [] for atom in structure: if atom.type == doped_atom_type: neighbors = coordination_shells(structure, nshells=5, center=atom.pos, tolerance=tolerance) nearest_neighbors.append(atom) atom.pseudo = 0 for item in neighbors[0]: atom = item[0] nearest_neighbors.append(atom) atom.pseudo = 0 atom = neighbors[0][0][0] nearest_name = atom.type print('\nSet *{0}* atoms as nearest neighbor.\n'.format( nearest_name)) flag = 0 for i in range(1, 5): temp = [] if flag != 0: break for item in neighbors[i]: atom = item[0] if nearest_name == atom.type: if not hasattr(atom, 'pseudo'): temp.append(atom) atom.pseudo = 0 else: flag = 1 nearest_neighbors.extend(temp) # nearest neighbor might be outside of CONTCAR with open('core structure.txt', 'w+') as fp: for atom in nearest_neighbors: fp.writelines(atom.type + ' ' + str(atom.pos) + '\n') for atom in structure: position = atom.pos d = np.linalg.norm(center - position) if d <= cluster_r: if hasattr(atom, 'pseudo'): cluster.append(Atom(atom.pos - center, atom.type, pseudo=0)) else: cluster.append( Atom(atom.pos - center, atom.type, pseudo=-1)) else: for atom in structure: d = np.linalg.norm(center - atom.pos) if d <= core_r: cluster.append(Atom(atom.pos - center, atom.type, pseudo=0)) elif d <= cluster_r: cluster.append(Atom(atom.pos - center, atom.type, pseudo=-1)) return cluster, center