def read_aims(filename): '''Method to read FHI-aims geometry files in phonopy context.''' cell = [] positions = [] fractional = [] symbols = [] magmoms = [] with open(filename) as f: while True: line = f.readline() if not line: break line = line.split() if line[0] == 'lattice_vector': cell.append([float(x) for x in line[1:4]]) elif line[0].startswith('atom'): fractional.append(line[0] == 'atom_frac') positions.append([float(x) for x in line[1:4]]) symbols.append(line[4]) elif line[0] == 'initial_moment': magmoms.append(float(line[1])) for n, pos in enumerate(positions): if fractional[n]: positions[n] = [ sum([pos[j] * cell[j][i] for j in range(3)]) for i in range(3) ] if len(magmoms) == len(positions): return PhonopyAtoms(cell=cell, symbols=symbols, positions=positions, magmoms=magmoms) else: return PhonopyAtoms(cell=cell, symbols=symbols, positions=positions)
def read_castep(filename): f_castep = open(filename) castep_in = CastepIn(f_castep.readlines()) f_castep.close() tags = castep_in.get_tags() # 1st stage is to create Atoms object ignoring Spin polarization. General case. cell = Atoms(cell=tags['lattice_vectors'], symbols=tags['atomic_species'], scaled_positions=tags['coordinates']) # Analyse spin states and add data to Atoms instance "cell" if ones exist magmoms = tags['magnetic_moments'] if magmoms is not None: # Print out symmetry information for magnetic cases # Original code from structure/symmetry.py symmetry = Symmetry(cell, symprec=1e-5) print( "CASTEP-interface: Magnetic structure, number of operations without spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("CASTEP-interface: Spacegroup without spin: %s" % symmetry.get_international_table()) cell.set_magnetic_moments(magmoms) symmetry = Symmetry(cell, symprec=1e-5) print( "CASTEP-interface: Magnetic structure, number of operations with spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("") return cell
def _create_supercell(self, unitcell, symprec): mat = self._supercell_matrix frame = self._get_surrounding_frame(mat) sur_cell, u2sur_map = self._get_simple_supercell(frame, unitcell) # Trim the simple supercell by the supercell matrix trim_frame = np.array([mat[0] / float(frame[0]), mat[1] / float(frame[1]), mat[2] / float(frame[2])]) supercell, sur2s_map, mapping_table = trim_cell(trim_frame, sur_cell, symprec) multi = supercell.get_number_of_atoms() // unitcell.get_number_of_atoms() if multi != determinant(self._supercell_matrix): print("Supercell creation failed.") print("Probably some atoms are overwrapped. " "The mapping table is give below.") print(mapping_table) Atoms.__init__(self) else: Atoms.__init__(self, numbers=supercell.get_atomic_numbers(), masses=supercell.get_masses(), magmoms=supercell.get_magnetic_moments(), scaled_positions=supercell.get_scaled_positions(), cell=supercell.get_cell(), pbc=True) self._u2s_map = np.arange(unitcell.get_number_of_atoms()) * multi self._u2u_map = dict([(j, i) for i, j in enumerate(self._u2s_map)]) self._s2u_map = np.array(u2sur_map)[sur2s_map] * multi
def read_crystal(filename): f_crystal = open(filename) crystal_in = CrystalIn(f_crystal.readlines()) f_crystal.close() tags = crystal_in.get_tags() cell = Atoms(cell=tags['lattice_vectors'], symbols=tags['atomic_species'], scaled_positions=tags['coordinates']) magmoms = tags['magnetic_moments'] if magmoms is not None: # Print out symmetry information for magnetic cases # Original code from structure/symmetry.py symmetry = Symmetry(cell, symprec=1e-5) print("CRYSTAL-interface: Magnetic structure, number of operations without spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("CRYSTAL-interface: Spacegroup without spin: %s" % symmetry.get_international_table()) cell.set_magnetic_moments(magmoms) symmetry = Symmetry(cell, symprec=1e-5) print("CRYSTAL-interface: Magnetic structure, number of operations with spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("") return cell, tags['conv_numbers']
def _create_supercell(self, unitcell, symprec): mat = self._supercell_matrix frame = self._get_surrounding_frame(mat) sur_cell, u2sur_map = self._get_simple_supercell(frame, unitcell) # Trim the simple supercell by the supercell matrix trim_frame = np.array([ mat[0] / float(frame[0]), mat[1] / float(frame[1]), mat[2] / float(frame[2]) ]) supercell, sur2s_map, mapping_table = trim_cell( trim_frame, sur_cell, symprec) num_satom = supercell.get_number_of_atoms() num_uatom = unitcell.get_number_of_atoms() multi = num_satom // num_uatom if multi != determinant(self._supercell_matrix): print("Supercell creation failed.") print("Probably some atoms are overwrapped. " "The mapping table is give below.") print(mapping_table) Atoms.__init__(self) else: Atoms.__init__(self, numbers=supercell.get_atomic_numbers(), masses=supercell.get_masses(), magmoms=supercell.get_magnetic_moments(), scaled_positions=supercell.get_scaled_positions(), cell=supercell.get_cell(), pbc=True) self._u2s_map = np.arange(num_uatom) * multi self._u2u_map = dict([(j, i) for i, j in enumerate(self._u2s_map)]) self._s2u_map = np.array(u2sur_map)[sur2s_map] * multi
def read_pwscf(filename): pwscf_in = PwscfIn(open(filename).readlines()) tags = pwscf_in.get_tags() lattice = tags['cell_parameters'] positions = [pos[1] for pos in tags['atomic_positions']] species = [pos[0] for pos in tags['atomic_positions']] mass_map = {} pp_map = {} for vals in tags['atomic_species']: mass_map[vals[0]] = vals[1] pp_map[vals[0]] = vals[2] masses = [mass_map[x] for x in species] pp_all_filenames = [pp_map[x] for x in species] unique_species = [] for x in species: if x not in unique_species: unique_species.append(x) numbers = [] is_unusual = False for x in species: if x in symbol_map: numbers.append(symbol_map[x]) else: numbers.append(-unique_species.index(x)) is_unusual = True if is_unusual: positive_numbers = [] for n in numbers: if n > 0: if n not in positive_numbers: positive_numbers.append(n) available_numbers = range(1, 119) for pn in positive_numbers: available_numbers.remove(pn) for i, n in enumerate(numbers): if n < 1: numbers[i] = available_numbers[-n] cell = Atoms(numbers=numbers, masses=masses, cell=lattice, scaled_positions=positions) else: cell = Atoms(numbers=numbers, cell=lattice, scaled_positions=positions) unique_symbols = [] pp_filenames = {} for i, symbol in enumerate(cell.get_chemical_symbols()): if symbol not in unique_symbols: unique_symbols.append(symbol) pp_filenames[symbol] = pp_all_filenames[i] return cell, pp_filenames
def read_crystal(filename): f_crystal = open(filename) crystal_in = CrystalIn(f_crystal.readlines()) f_crystal.close() tags = crystal_in.get_tags() cell = Atoms(cell=tags['lattice_vectors'], symbols=tags['atomic_species'], scaled_positions=tags['coordinates']) magmoms = tags['magnetic_moments'] if magmoms is not None: # Print out symmetry information for magnetic cases # Original code from structure/symmetry.py symmetry = Symmetry(cell, symprec=1e-5) print( "CRYSTAL-interface: Magnetic structure, number of operations without spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("CRYSTAL-interface: Spacegroup without spin: %s" % symmetry.get_international_table()) cell.set_magnetic_moments(magmoms) symmetry = Symmetry(cell, symprec=1e-5) print( "CRYSTAL-interface: Magnetic structure, number of operations with spin: %d" % len(symmetry.get_symmetry_operations()['rotations'])) print("") return cell, tags['conv_numbers']
def setUp(self): self._cells = [] symbols = ['Si'] * 2 + ['O'] * 4 lattice = [[4.65, 0, 0], [0, 4.75, 0], [0, 0, 3.25]] points = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5], [0.3, 0.3, 0.0], [0.7, 0.7, 0.0], [0.2, 0.8, 0.5], [0.8, 0.2, 0.5]] self._cells.append( PhonopyAtoms(cell=lattice, scaled_positions=points, symbols=symbols)) symbols = ['Si'] * 2 lattice = [[0, 2.73, 2.73], [2.73, 0, 2.73], [2.73, 2.73, 0]] points = [[0.75, 0.75, 0.75], [0.5, 0.5, 0.5]] self._cells.append( PhonopyAtoms(cell=lattice, scaled_positions=points, symbols=symbols)) self._smats = [] self._smats.append(np.diag([1, 2, 3])) self._smats.append([[-1, 1, 1], [1, -1, 1], [1, 1, -1]]) self._fnames = ("SiO2-123.yaml", "Si-conv.yaml")
def test_magmom(convcell_cr: PhonopyAtoms): """Test symmetry search with hmagnetic moments.""" symprec = 1e-5 symmetry_nonspin = Symmetry(convcell_cr, symprec=symprec) atom_map_nonspin = symmetry_nonspin.get_map_atoms() len_sym_nonspin = len(symmetry_nonspin.symmetry_operations["rotations"]) spin = [1, -1] cell_withspin = convcell_cr.copy() cell_withspin.magnetic_moments = spin symmetry_withspin = Symmetry(cell_withspin, symprec=symprec) atom_map_withspin = symmetry_withspin.get_map_atoms() len_sym_withspin = len(symmetry_withspin.symmetry_operations["rotations"]) broken_spin = [1, -2] cell_brokenspin = convcell_cr.copy() cell_brokenspin = convcell_cr.copy() cell_brokenspin.magnetic_moments = broken_spin symmetry_brokenspin = Symmetry(cell_brokenspin, symprec=symprec) atom_map_brokenspin = symmetry_brokenspin.get_map_atoms() len_sym_brokenspin = len( symmetry_brokenspin.symmetry_operations["rotations"]) assert (atom_map_nonspin == atom_map_withspin).all() assert (atom_map_nonspin != atom_map_brokenspin).any() assert len_sym_nonspin == len_sym_withspin assert len_sym_nonspin != len_sym_brokenspin
def standardize_cell(structure): import spglib from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy.structure.atoms import atom_data bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) structure_data = (structure.cell, bulk.get_scaled_positions(), bulk.get_atomic_numbers()) #lattice, refined_positions, numbers = spglib.refine_cell(structure_data, symprec=1e-5) lattice, standardized_positions, numbers = spglib.standardize_cell(structure_data, symprec=1e-5, to_primitive=False, no_idealize=False) symbols = [atom_data[i][1] for i in numbers] # print lattice, standardized_positions, numbers # print [site.kind_name for site in structure.sites] standardized_bulk = PhonopyAtoms(symbols=symbols, scaled_positions=standardized_positions, cell=lattice) # create new aiida structure object standarized = StructureData(cell=standardized_bulk.get_cell()) for position, symbol in zip(standardized_bulk.get_positions(), standardized_bulk.get_chemical_symbols()): standarized.append_atom(position=position, symbols=symbol) return {'standardized_structure': standarized}
def _distribute_forces(supercell, disp, forces, filename, symprec): natom = supercell.get_number_of_atoms() lattice = supercell.get_cell() symbols = supercell.get_chemical_symbols() positions = supercell.get_positions() positions[disp[0]] += disp[1] cell = Atoms(cell=lattice, positions=positions, symbols=symbols, pbc=True) symmetry = Symmetry(cell, symprec) independent_atoms = symmetry.get_independent_atoms() # Rotation matrices in Cartesian rotations = [] for r in symmetry.get_symmetry_operations()["rotations"]: rotations.append(similarity_transformation(lattice.T, r)) map_operations = symmetry.get_map_operations() map_atoms = symmetry.get_map_atoms() atoms_in_dot_scf = _get_independent_atoms_in_dot_scf(filename) if len(forces) != len(atoms_in_dot_scf): print("%s does not contain necessary information." % filename) print('Plese check if there are "FGL" lines with') print('"total forces" are required.') return False if len(atoms_in_dot_scf) == natom: print("It is assumed that there is no symmetrically-equivalent " "atoms in ") print("'%s' at wien2k calculation." % filename) force_set = forces elif len(forces) != len(independent_atoms): print("Non-equivalent atoms of %s could not be recognized by phonopy." % filename) return False else: # 1. Transform wien2k forces to those on independent atoms indep_atoms_to_wien2k = [] forces_remap = [] for i, pos_wien2k in enumerate(atoms_in_dot_scf): for j, pos in enumerate(cell.get_scaled_positions()): diff = pos_wien2k - pos diff -= np.rint(diff) if (abs(diff) < symprec).all(): forces_remap.append(np.dot(rotations[map_operations[j]], forces[i])) indep_atoms_to_wien2k.append(map_atoms[j]) break if len(forces_remap) != len(forces): print("Atomic position mapping between Wien2k and phonopy failed.") print("If you think this is caused by a bug of phonopy") print("please report it in the phonopy mainling list.") return False # 2. Distribute forces from independent to dependent atoms. force_set = [] for i in range(natom): j = indep_atoms_to_wien2k.index(map_atoms[i]) force_set.append(np.dot(rotations[map_operations[i]].T, forces_remap[j])) return force_set
def test_get_primitive_convcell_Cr(convcell_cr: PhonopyAtoms, helper_methods): """Test get_primitive by NaCl.""" convcell_cr.magnetic_moments = [1, -1] smat = [[2, 0, 0], [0, 2, 0], [0, 0, 2]] scell = get_supercell(convcell_cr, smat, is_old_style=True) pmat = np.linalg.inv(smat) pcell = get_primitive(scell, pmat) helper_methods.compare_cells(convcell_cr, pcell) convcell_cr.magnetic_moments = None
def read_siesta(filename): """Read crystal structure.""" siesta_in = SiestaIn(open(filename).read()) numbers = siesta_in._tags["atomicnumbers"] alat = siesta_in._tags["latticeconstant"] lattice = siesta_in._tags["latticevectors"] positions = siesta_in._tags["atomiccoordinates"] atypes = siesta_in._tags["chemicalspecieslabel"] cell = Atoms(numbers=numbers, cell=lattice, scaled_positions=positions) coordformat = siesta_in._tags["atomiccoordinatesformat"] if coordformat == "fractional" or coordformat == "scaledbylatticevectors": cell.set_scaled_positions(positions) elif coordformat == "scaledcartesian": cell.set_positions(np.array(positions) * alat) elif coordformat == "notscaledcartesianang" or coordformat == "ang": cell.set_positions(np.array(positions) / Bohr) elif coordformat == "notscaledcartesianbohr" or coordformat == "bohr": cell.set_positions(np.array(positions)) else: print("The format %s for the AtomicCoordinatesFormat is not " "implemented." % coordformat) sys.exit(1) return cell, atypes
def _primitive_cell(self, supercell): trimed_cell, p2s_map, mapping_table = trim_cell( self._primitive_matrix, supercell, self._symprec) Atoms.__init__(self, numbers=trimed_cell.get_atomic_numbers(), masses=trimed_cell.get_masses(), magmoms=trimed_cell.get_magnetic_moments(), scaled_positions=trimed_cell.get_scaled_positions(), cell=trimed_cell.get_cell(), pbc=True) self._p2s_map = np.array(p2s_map, dtype='intc')
def _create_supercell(self, unitcell, symprec): mat = self._supercell_matrix if self._is_old_style: P = None multi = self._get_surrounding_frame(mat) # trim_fram is to trim overlapping atoms. trim_frame = np.array([ mat[0] / float(multi[0]), mat[1] / float(multi[1]), mat[2] / float(multi[2]) ]) else: # In the new style, it is unnecessary to trim atoms, if (np.diag(np.diagonal(mat)) != mat).any(): snf = SNF3x3(mat) snf.run() P = snf.P multi = np.diagonal(snf.A) else: P = None multi = np.diagonal(mat) trim_frame = np.eye(3) sur_cell, u2sur_map = self._get_simple_supercell(unitcell, multi, P) trimmed_cell_ = _trim_cell(trim_frame, sur_cell, symprec) if trimmed_cell_: supercell, sur2s_map, mapping_table = trimmed_cell_ else: return False num_satom = supercell.get_number_of_atoms() num_uatom = unitcell.get_number_of_atoms() N = num_satom // num_uatom if N != determinant(self._supercell_matrix): print("Supercell creation failed.") print("Probably some atoms are overwrapped. " "The mapping table is give below.") print(mapping_table) PhonopyAtoms.__init__(self) else: PhonopyAtoms.__init__( self, numbers=supercell.get_atomic_numbers(), masses=supercell.get_masses(), magmoms=supercell.get_magnetic_moments(), scaled_positions=supercell.get_scaled_positions(), cell=supercell.get_cell(), pbc=True) self._u2s_map = np.arange(num_uatom) * N self._u2u_map = {j: i for i, j in enumerate(self._u2s_map)} self._s2u_map = np.array(u2sur_map)[sur2s_map] * N
def test_get_supercell_Cr(convcell_cr: PhonopyAtoms, helper_methods): """Test of get_supercell using SNF by Cr with magnetic moments.""" convcell_cr.magnetic_moments = [1, -1] smat = [[-1, 1, 1], [1, -1, 1], [1, 1, -1]] scell = get_supercell(convcell_cr, smat, is_old_style=True) np.testing.assert_allclose( scell.magnetic_moments, [1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0], atol=1e-8, ) scell_snf = get_supercell(convcell_cr, smat, is_old_style=False) helper_methods.compare_cells(scell, scell_snf) convcell_cr.magnetic_moments = None
def _primitive_cell(self, supercell): trimed_cell, p2s_map, mapping_table = trim_cell(self._primitive_matrix, supercell, self._symprec) Atoms.__init__(self, numbers=trimed_cell.get_atomic_numbers(), masses=trimed_cell.get_masses(), magmoms=trimed_cell.get_magnetic_moments(), scaled_positions=trimed_cell.get_scaled_positions(), cell=trimed_cell.get_cell(), pbc=True) self._p2s_map = np.array(p2s_map, dtype='intc')
def _create_supercell(self, unitcell, symprec): mat = self._supercell_matrix if self._is_old_style: P = None multi = self._get_surrounding_frame(mat) # trim_fram is to trim overlapping atoms. trim_frame = np.array([mat[0] / float(multi[0]), mat[1] / float(multi[1]), mat[2] / float(multi[2])]) else: # In the new style, it is unnecessary to trim atoms, if (np.diag(np.diagonal(mat)) != mat).any(): snf = SNF3x3(mat) snf.run() P = snf.P multi = np.diagonal(snf.A) else: P = None multi = np.diagonal(mat) trim_frame = np.eye(3) sur_cell, u2sur_map = self._get_simple_supercell(unitcell, multi, P) trimmed_cell_ = _trim_cell(trim_frame, sur_cell, symprec) if trimmed_cell_: supercell, sur2s_map, mapping_table = trimmed_cell_ else: return False num_satom = supercell.get_number_of_atoms() num_uatom = unitcell.get_number_of_atoms() N = num_satom // num_uatom if N != determinant(self._supercell_matrix): print("Supercell creation failed.") print("Probably some atoms are overwrapped. " "The mapping table is give below.") print(mapping_table) PhonopyAtoms.__init__(self) else: PhonopyAtoms.__init__( self, numbers=supercell.get_atomic_numbers(), masses=supercell.get_masses(), magmoms=supercell.get_magnetic_moments(), scaled_positions=supercell.get_scaled_positions(), cell=supercell.get_cell(), pbc=True) self._u2s_map = np.arange(num_uatom) * N self._u2u_map = {j: i for i, j in enumerate(self._u2s_map)} self._s2u_map = np.array(u2sur_map)[sur2s_map] * N
def get_cell_settings( supercell_matrix=None, primitive_matrix=None, unitcell=None, supercell=None, unitcell_filename=None, supercell_filename=None, calculator=None, symprec=1e-5, log_level=0, ): """Return crystal structures.""" optional_structure_info = None if primitive_matrix is None or (type(primitive_matrix) is str and primitive_matrix == "auto"): # noqa E129 pmat = "auto" else: pmat = primitive_matrix if unitcell_filename is not None: cell, optional_structure_info = _read_crystal_structure( filename=unitcell_filename, interface_mode=calculator) smat = supercell_matrix if log_level: print('Unit cell structure was read from "%s".' % optional_structure_info[0]) elif supercell_filename is not None: cell, optional_structure_info = read_crystal_structure( filename=supercell_filename, interface_mode=calculator) smat = np.eye(3, dtype="intc", order="C") if log_level: print('Supercell structure was read from "%s".' % optional_structure_info[0]) elif unitcell is not None: cell = PhonopyAtoms(atoms=unitcell) smat = supercell_matrix elif supercell is not None: cell = PhonopyAtoms(atoms=supercell) smat = np.eye(3, dtype="intc", order="C") else: raise RuntimeError("Cell has to be specified.") if optional_structure_info is not None and cell is None: filename = optional_structure_info[0] msg = "'%s' could not be found." % filename raise FileNotFoundError(msg) pmat = get_primitive_matrix(pmat, symprec=symprec) return cell, smat, pmat
def phonopyAtoms_to_aseAtoms(PhonopyAtoms, pbc=[True, True, True]): aseAtoms = ase.Atoms(symbols=PhonopyAtoms.get_chemical_symbols(), positions=PhonopyAtoms.get_positions(), cell=PhonopyAtoms.get_cell()) aseAtoms.set_pbc(pbc) aseAtoms.set_masses(PhonopyAtoms.get_masses()) Atomic_numbers = PhonopyAtoms.get_atomic_numbers() Atomic_type_tags = np.zeros(np.shape(Atomic_numbers)) atomic_type_unique = np.unique(Atomic_numbers) for i, iZ in enumerate(atomic_type_unique): Atomic_type_tags[Atomic_numbers == iZ] = i aseAtoms.set_tags(Atomic_type_tags) aseAtoms.set_initial_charges() return aseAtoms
def get_cell_settings(phonopy_yaml=None, supercell_matrix=None, primitive_matrix=None, unitcell=None, supercell=None, unitcell_filename=None, supercell_filename=None, calculator=None, symprec=1e-5, log_level=0): optional_structure_info = None if (primitive_matrix is None or (type(primitive_matrix) is str and primitive_matrix == "auto")): pmat = 'auto' else: pmat = primitive_matrix if unitcell_filename is not None: cell, optional_structure_info = _read_crystal_structure( filename=unitcell_filename, interface_mode=calculator) smat = supercell_matrix if log_level: print("Unit cell structure was read from \"%s\"." % optional_structure_info[0]) elif supercell_filename is not None: cell, optional_structure_info = read_crystal_structure( filename=supercell_filename, interface_mode=calculator) smat = np.eye(3, dtype='intc', order='C') if log_level: print("Supercell structure was read from \"%s\"." % optional_structure_info[0]) elif unitcell is not None: cell = PhonopyAtoms(atoms=unitcell) smat = supercell_matrix elif supercell is not None: cell = PhonopyAtoms(atoms=supercell) smat = np.eye(3, dtype='intc', order='C') else: raise RuntimeError("Cell has to be specified.") if optional_structure_info is not None and cell is None: filename = optional_structure_info[0] msg = "'%s' could not be found." % filename raise FileNotFoundError(msg) pmat = _get_primitive_matrix(pmat, cell, symprec) return cell, smat, pmat
def read_phonopy(sposcar='SPOSCAR', sc_mat=np.eye(3), force_constants=None, disp_yaml=None, force_sets=None): if force_constants is None and (disp_yaml is None or force_sets is None): raise ValueError( "Either FORCE_CONSTANTS or (disp.yaml&FORCE_SETS) file should be provided." ) atoms = read(sposcar) #vesta_view(atoms) primitive_matrix = inv(sc_mat) bulk = PhonopyAtoms(symbols=atoms.get_chemical_symbols(), scaled_positions=atoms.get_scaled_positions(), cell=atoms.get_cell()) phonon = Phonopy( bulk, supercell_matrix=np.eye(3), primitive_matrix=primitive_matrix, #factor=factor, #symprec=symprec ) if disp_yaml is not None: disp = parse_disp_yaml(filename=disp_yaml) phonon.set_displacement_dataset(disp) if force_sets is not None: fc = parse_FORCE_SETS(filename=force_sets) phonon.set_forces(fc) fc = parse_FORCE_CONSTANTS(force_constants) phonon.set_force_constants(fc) return phonon
def get_properties_from_phonopy(structure, phonopy_input, force_constants): """ Calculate DOS and thermal properties using phonopy (locally) :param structure: Aiida StructureData Object :param phonopy_input: Aiida Parametersdata object containing a dictionary with the data needed to run phonopy: supercells matrix, primitive matrix and q-points mesh. :param force_constants: :return: """ from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonopy_input = phonopy_input.get_dict() force_constants = force_constants.get_array('force_constants') phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive']) phonon.set_force_constants(force_constants) #Normalization factor primitive to unit cell normalization_factor = phonon.unitcell.get_number_of_atoms( ) / phonon.primitive.get_number_of_atoms() phonon.set_mesh(phonopy_input['mesh'], is_eigenvectors=True, is_mesh_symmetry=False) phonon.set_total_DOS() phonon.set_partial_DOS() # get DOS (normalized to unit cell) total_dos = phonon.get_total_DOS() * normalization_factor partial_dos = phonon.get_partial_DOS() * normalization_factor # Stores DOS data in DB as a workflow result dos = ArrayData() dos.set_array('frequency', total_dos[0]) dos.set_array('total_dos', total_dos[1]) dos.set_array('partial_dos', partial_dos[1]) #THERMAL PROPERTIES (per primtive cell) phonon.set_thermal_properties() t, free_energy, entropy, cv = phonon.get_thermal_properties() # Stores thermal properties (per unit cell) data in DB as a workflow result thermal_properties = ArrayData() thermal_properties.set_array('temperature', t) thermal_properties.set_array('free_energy', free_energy * normalization_factor) thermal_properties.set_array('entropy', entropy * normalization_factor) thermal_properties.set_array('cv', cv * normalization_factor) return {'thermal_properties': thermal_properties, 'dos': dos}
def test_structure_to_phonopy_atoms(sc_structure): actual = structure_to_phonopy_atoms(sc_structure) expected = PhonopyAtoms(symbols=["H"], cell=np.array([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]), scaled_positions=np.array([[0.0, 0.0, 0.0]])) assert_same_phonopy_atoms(actual, expected)
def get_phonon(structure, NAC=False, setup_forces=True, custom_supercell=None): super_cell_phonon = structure.get_supercell_phonon() if not(isinstance(custom_supercell, type(None))): super_cell_phonon = custom_supercell #Preparing the bulk type object bulk = PhonopyAtoms(symbols=structure.get_atomic_types(), scaled_positions=structure.get_scaled_positions(), cell=structure.get_cell().T) phonon = Phonopy(bulk, super_cell_phonon, primitive_matrix=structure.get_primitive_matrix()) # Non Analytical Corrections (NAC) from Phonopy [Frequencies only, eigenvectors no affected by this option] if NAC: print("Phonopy warning: Using Non Analytical Corrections") get_is_symmetry = True #from phonopy: settings.get_is_symmetry() primitive = phonon.get_primitive() nac_params = parse_BORN(primitive, get_is_symmetry) phonon.set_nac_params(nac_params=nac_params) if setup_forces: if not structure.forces_available(): # if not np.array(structure.get_force_constants()).any() and not np.array(structure.get_force_sets()).any(): print('No force sets/constants available!') exit() if np.array(structure.get_force_constants()).any(): phonon.set_force_constants(structure.get_force_constants()) else: phonon.set_displacement_dataset(structure.get_force_sets()) phonon.produce_force_constants(computation_algorithm="svd") return phonon
def analyse_phonopy_equivalent_atoms(atoms, symprec=1e-5, angle_tolerance=-1.0): """ Args: (read phonopy.structure.spglib for more details) symprec: float: Symmetry search tolerance in the unit of length. angle_tolerance: float: Symmetry search tolerance in the unit of angle deg. If the value is negative, an internally optimized routine is used to judge symmetry. """ s.publication_add(publication()) positions = atoms.get_scaled_positions() cell = atoms.cell types = atoms.get_chemical_symbols() types = list(types) natom = len(types) positions = np.reshape(np.array(positions), (natom, 3)) cell = np.reshape(np.array(cell), (3, 3)) unitcell = PhonopyAtoms(symbols=types, cell=cell, scaled_positions=positions) ops = spg.get_symmetry(unitcell, symprec=symprec, angle_tolerance=angle_tolerance) return ops['equivalent_atoms']
def get_cell_from_disp_yaml(dataset): """Read cell from disp.yaml like file.""" if "lattice" in dataset: lattice = dataset["lattice"] if "points" in dataset: data_key = "points" pos_key = "coordinates" elif "atoms" in dataset: data_key = "atoms" pos_key = "position" else: data_key = None pos_key = None try: positions = [x[pos_key] for x in dataset[data_key]] except KeyError: msg = ('"disp.yaml" format is too old. ' 'Please re-create it as "phonopy_disp.yaml" to contain ' "supercell crystal structure information.") raise RuntimeError(msg) symbols = [x["symbol"] for x in dataset[data_key]] cell = PhonopyAtoms(cell=lattice, scaled_positions=positions, symbols=symbols, pbc=True) return cell else: return get_cell_from_disp_yaml(dataset["supercell"])
def check_symmetry(phonon, optional_structure_info): # Assumed that primitive cell is the cell that user is interested in. print(_get_symmetry_yaml(phonon.primitive, phonon.primitive_symmetry, phonon.version)) if phonon.unitcell.magnetic_moments is None: base_fname = get_default_cell_filename(phonon.calculator) symprec = phonon.primitive_symmetry.get_symmetry_tolerance() (bravais_lattice, bravais_pos, bravais_numbers) = spglib.refine_cell(phonon.primitive, symprec) bravais = PhonopyAtoms(numbers=bravais_numbers, scaled_positions=bravais_pos, cell=bravais_lattice) filename = 'B' + base_fname print("# Symmetrized conventional unit cell is written into %s." % filename) trans_mat = guess_primitive_matrix(bravais, symprec=symprec) primitive = get_primitive(bravais, trans_mat, symprec=symprec) write_crystal_structure( filename, bravais, interface_mode=phonon.calculator, optional_structure_info=optional_structure_info) filename = 'P' + base_fname print("# Symmetrized primitive is written into %s." % filename) write_crystal_structure( filename, primitive, interface_mode=phonon.calculator, optional_structure_info=optional_structure_info)
def get_commensurate_points(supercell_matrix): # wrt primitive cell """Commensurate q-points are returned. Parameters ---------- supercell_matrix : array_like Supercell matrix with respect to primitive cell basis vectors. shape=(3, 3), dtype=int Returns ------- commensurate_points : ndarray Commensurate points corresponding to supercell matrix. shape=(N, 3), dtype='double', order='C' where N = det(supercell_matrix) """ smat = np.array(supercell_matrix, dtype=int) rec_primitive = PhonopyAtoms(numbers=[1], scaled_positions=[[0, 0, 0]], cell=np.diag([1, 1, 1]), pbc=True) rec_supercell = get_supercell(rec_primitive, smat.T) q_pos = rec_supercell.scaled_positions return np.array(np.where(q_pos > 1 - 1e-15, q_pos - 1, q_pos), dtype='double', order='C')
def phonons(model, bulk, supercell, dx, mesh=None, points=None, n_points=50): import model unitcell = PhonopyAtoms(symbols=bulk.get_chemical_symbols(), cell=bulk.get_cell(), scaled_positions=bulk.get_scaled_positions()) phonon = Phonopy(unitcell, supercell) phonon.generate_displacements(distance=dx) sets_of_forces = [] for s in phonon.get_supercells_with_displacements(): at = Atoms(cell=s.get_cell(), symbols=s.get_chemical_symbols(), scaled_positions=s.get_scaled_positions(), pbc=3 * [True]) at.set_calculator(model.calculator) sets_of_forces.append(at.get_forces()) phonon.set_forces(sets_of_forces=sets_of_forces) phonon.produce_force_constants() properties = {} if mesh is not None: phonon.set_mesh(mesh, is_gamma_center=True) qpoints, weights, frequencies, eigvecs = phonon.get_mesh() properties["frequencies"] = frequencies.tolist() properties["weights"] = weights.tolist() if points is not None: bands = [] for i in range(len(points) - 1): band = [] for r in np.linspace(0, 1, n_points): band.append(points[i] + (points[i + 1] - points[i]) * r) bands.append(band) phonon.set_band_structure(bands, is_eigenvectors=True, is_band_connection=False) band_q_points, band_distances, band_frequencies, band_eigvecs = phonon.get_band_structure( ) band_distance_max = np.max(band_distances) band_distances = [(_b / band_distance_max).tolist() for _b in band_distances] band_frequencies = [_b.tolist() for _b in band_frequencies] properties["band_q_points"] = band_q_points properties["band_distances"] = band_distances properties["band_frequencies"] = band_frequencies properties["band_eigvecs"] = band_eigvecs properties["phonopy"] = phonon return properties
def get_cell_from_disp_yaml(dataset): if 'lattice' in dataset: lattice = dataset['lattice'] if 'points' in dataset: data_key = 'points' pos_key = 'coordinates' elif 'atoms' in dataset: data_key = 'atoms' pos_key = 'position' else: data_key = None pos_key = None try: positions = [x[pos_key] for x in dataset[data_key]] except KeyError: msg = ("\"disp.yaml\" format is too old. " "Please re-create it as \"phonopy_disp.yaml\" to contain " "supercell crystal structure information.") raise RuntimeError(msg) symbols = [x['symbol'] for x in dataset[data_key]] cell = PhonopyAtoms(cell=lattice, scaled_positions=positions, symbols=symbols, pbc=True) return cell else: return get_cell_from_disp_yaml(dataset['supercell'])
def get_equivalent_q_points_by_symmetry(q_point, structure): from phonopy.structure.symmetry import Symmetry bulk = PhonopyAtoms(symbols=structure.get_atomic_elements(), scaled_positions=structure.get_scaled_positions(), cell=structure.get_cell().T) tot_points = [] for operation_matrix in Symmetry(bulk).get_reciprocal_operations(): operation_matrix_q = np.dot( np.linalg.inv(structure.get_primitive_matrix()), operation_matrix.T) operation_matrix_q = np.dot(operation_matrix_q, structure.get_primitive_matrix()) q_point_test = np.dot(q_point, operation_matrix_q) if (q_point_test >= 0).all(): tot_points.append(q_point_test) # print tot_points # print(np.vstack({tuple(row) for row in tot_points})) return np.vstack({tuple(row) for row in tot_points})
def get_force_sets_inline(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive'], symprec=phonopy_input['symmetry_precision']) phonon.generate_displacements(distance=phonopy_input['distance']) # Build data_sets from forces of supercells with displacments data_sets = phonon.get_displacement_dataset() for i, first_atoms in enumerate(data_sets['first_atoms']): first_atoms['forces'] = kwargs.pop( 'force_{}'.format(i)).get_array('forces')[-1] data = ArrayData() data.set_array('force_sets', np.array(data_sets)) return {'phonopy_output': data}
def create_supercells_with_displacements_inline(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() # Generate phonopy phonon object bulk = PhonopyAtoms(symbols=[site.kind_name for site in structure.sites], positions=[site.position for site in structure.sites], cell=structure.cell) phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive'], symprec=phonopy_input['symmetry_precision']) phonon.generate_displacements(distance=phonopy_input['distance']) cells_with_disp = phonon.get_supercells_with_displacements() # Transform cells to StructureData and set them ready to return disp_cells = {} for i, phonopy_supercell in enumerate(cells_with_disp): supercell = StructureData(cell=phonopy_supercell.get_cell()) for symbol, position in zip(phonopy_supercell.get_chemical_symbols(), phonopy_supercell.get_positions()): supercell.append_atom(position=position, symbols=symbol) disp_cells["structure_{}".format(i)] = supercell return disp_cells
def _get_cell(self, lattice, points, symbols, masses=None): if lattice: _lattice = lattice else: _lattice = None if points: _points = points else: _points = None if symbols: _symbols = symbols else: _symbols = None if masses: _masses = masses else: _masses = None if _lattice and _points and _symbols: return PhonopyAtoms(symbols=_symbols, cell=_lattice, masses=_masses, scaled_positions=_points) else: return None
def read_siesta(filename): siesta_in = SiestaIn(open(filename).read()) numbers = siesta_in._tags["atomicnumbers"] alat = siesta_in._tags["latticeconstant"] lattice = siesta_in._tags["latticevectors"] positions = siesta_in._tags["atomiccoordinates"] atypes = siesta_in._tags["chemicalspecieslabel"] cell = Atoms(numbers=numbers, cell=lattice, scaled_positions=positions) coordformat = siesta_in._tags["atomiccoordinatesformat"] if coordformat == "fractional" or coordformat == "scaledbylatticevectors": cell.set_scaled_positions(positions) elif coordformat == "scaledcartesian": cell.set_positions(np.array(positions) * alat) elif coordformat == "notscaledcartesianang" or coordformat == "ang": cell.set_positions(np.array(positions) / Bohr) elif coordformat == "notscaledcartesianbohr" or coordformat == "bohr": cell.set_positions(np.array(positions)) else: print("The format %s for the AtomicCoordinatesFormat is not " "implemented." % coordformat) sys.exit(1) return cell, atypes
class Phonopy(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=None, factor=VaspToTHz, is_auto_displacements=None, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): if is_auto_displacements is not None: print("Warning: \'is_auto_displacements\' argument is obsolete.") if is_auto_displacements is False: print("Sets of displacements are not created as default.") else: print("Use \'generate_displacements\' method explicitly to " "create sets of displacements.") if distance is not None: print("Warning: \'distance\' keyword argument is obsolete at " "Phonopy instantiation.") print("Specify \'distance\' keyword argument when calling " "\'generate_displacements\'") print("method (See the Phonopy API document).") self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = Atoms(atoms=unitcell) self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def get_version(self): return __version__ def get_primitive(self): return self._primitive primitive = property(get_primitive) def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def get_symmetry(self): """return symmetry of supercell""" return self._symmetry symmetry = property(get_symmetry) def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_supercell_matrix(self): return self._supercell_matrix def get_primitive_matrix(self): return self._primitive_matrix def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def get_displacement_dataset(self): return self._displacement_dataset def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_supercells_with_displacements(self): if self._displacement_dataset is None: return None else: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def get_nac_params(self): return self._nac_params def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_unitcell(self, unitcell): self._unitcell = unitcell self._build_supercell() self._build_primitive_cell() self._search_symmetry() self._search_primitive_symmetry() self._displacement_dataset = None def set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._set_dynamical_matrix() def set_nac_params(self, nac_params=None): self._nac_params = nac_params self._set_dynamical_matrix() def set_displacement_dataset(self, displacement_dataset): """ displacement_dataset: {'natom': number_of_atoms_in_supercell, 'first_atoms': [ {'number': atom index of displaced atom, 'displacement': displacement in Cartesian coordinates, 'direction': displacement direction with respect to axes 'forces': forces on atoms in supercell}, {...}, ...]} """ self._displacement_dataset = displacement_dataset self._displacements = [] self._displacement_directions = [] for disp in self._displacement_dataset['first_atoms']: x = disp['displacement'] self._displacements.append([disp['number'], x[0], x[1], x[2]]) if 'direction' in disp: y = disp['direction'] self._displacement_directions.append( [disp['number'], y[0], y[1], y[2]]) if not self._displacement_directions: self._displacement_directions = None def set_forces(self, sets_of_forces): """ sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] """ for disp, forces in zip( self._displacement_dataset['first_atoms'], sets_of_forces): disp['forces'] = forces def set_force_constants(self, force_constants): self._force_constants = force_constants self._set_dynamical_matrix() def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) self._set_dynamical_matrix() def set_dynamical_matrix(self): self._set_dynamical_matrix() def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset) def produce_force_constants(self, forces=None, calculate_full_force_constants=True, computation_algorithm="svd"): if forces is not None: self.set_forces(forces) # A primitive check if 'forces' key is in displacement_dataset. for disp in self._displacement_dataset['first_atoms']: if 'forces' not in disp: return False if calculate_full_force_constants: self._run_force_constants_from_forces( decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self._run_force_constants_from_forces( distributed_atom_list=p2s_map, decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) self._set_dynamical_matrix() return True def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) self._set_dynamical_matrix() def symmetrize_force_constants_by_space_group(self): from phonopy.harmonic.force_constants import (set_tensor_symmetry, set_tensor_symmetry_PJ) set_tensor_symmetry_PJ(self._force_constants, self._supercell.get_cell().T, self._supercell.get_scaled_positions(), self._symmetry) self._set_dynamical_matrix() ##################### # Phonon properties # ##################### # Single q-point def get_dynamical_matrix_at_q(self, q): self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors # Band structure def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._band_structure = None return False self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) return True def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, labels=None): import matplotlib.pyplot as plt if labels: from matplotlib import rc rc('text', usetex=True) self._band_structure.plot(plt, labels=labels) return plt def write_yaml_band_structure(self, labels=None, comment=None, filename="band.yaml"): self._band_structure.write_yaml(labels=labels, comment=comment, filename=filename) # Sampling mesh def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_mesh_symmetry=True, is_eigenvectors=False, is_gamma_center=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._mesh = None return False self._mesh = Mesh( self._dynamical_matrix, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_mesh_symmetry, is_eigenvectors=is_eigenvectors, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor, use_lapack_solver=self._use_lapack_solver) return True def get_mesh(self): if self._mesh is None: return None else: return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def get_mesh_grid_info(self): if self._mesh is None: return None else: return (self._mesh.get_grid_address(), self._mesh.get_ir_grid_points(), self._mesh.get_grid_mapping_table()) def write_hdf5_mesh(self): self._mesh.write_hdf5() def write_yaml_mesh(self): self._mesh.write_yaml() # Plot band structure and DOS (PDOS) together def plot_band_structure_and_dos(self, pdos_indices=None, labels=None): import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec if labels: from matplotlib import rc rc('text', usetex=True) plt.figure(figsize=(10, 6)) gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1]) ax1 = plt.subplot(gs[0, 0]) self._band_structure.plot(plt, labels=labels) ax2 = plt.subplot(gs[0, 1], sharey=ax1) plt.subplots_adjust(wspace=0.03) plt.setp(ax2.get_yticklabels(), visible=False) if pdos_indices is None: self._total_dos.plot(plt, ylabel="", draw_grid=False, flip_xy=True) else: self._pdos.plot(plt, indices=pdos_indices, ylabel="", draw_grid=False, flip_xy=True) return plt # DOS def set_total_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False): if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before DOS calculation.") self._total_dos = None return False total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(freq_min, freq_max, freq_pitch) total_dos.run() self._total_dos = total_dos return True def get_total_DOS(self): """ Retern frequencies and total dos. The first element is freqs and the second is total dos. frequencies: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): import matplotlib.pyplot as plt self._total_dos.plot(plt) return plt def write_total_DOS(self): self._total_dos.write() # PDOS def set_partial_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False, direction=None, xyz_projection=False): self._pdos = None if self._mesh is None: print("Warning: \'set_mesh\' has to be called before " "PDOS calculation.") return False if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False num_grid = np.prod(self._mesh.get_mesh_numbers()) if num_grid != len(self._mesh.get_ir_grid_points()): print("Warning: \'set_mesh\' has to be called with " "is_mesh_symmetry=False.") return False if direction is not None: direction_cart = np.dot(direction, self._primitive.get_cell()) else: direction_cart = None self._pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method, direction=direction_cart, xyz_projection=xyz_projection) self._pdos.set_draw_area(freq_min, freq_max, freq_pitch) self._pdos.run() return True def get_partial_DOS(self): """ Retern frequencies and partial_dos. The first element is freqs and the second is partial_dos. frequencies: [freq1, freq2, ...] partial_dos: [[atom1-freq1, atom1-freq2, ...], [atom2-freq1, atom2-freq2, ...], ...] """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): import matplotlib.pyplot as plt self._pdos.plot(plt, indices=pdos_indices, legend=legend) return plt def write_partial_DOS(self): self._pdos.write() # Thermal property def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, temperatures=None, is_projection=False, band_indices=None, cutoff_frequency=None): if self._mesh is None: print("Warning: set_mesh has to be done before " "set_thermal_properties") return False else: tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, band_indices=band_indices, cutoff_frequency=cutoff_frequency) if temperatures is None: tp.set_temperature_range(t_step=t_step, t_max=t_max, t_min=t_min) else: tp.set_temperatures(temperatures) tp.run() self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): import matplotlib.pyplot as plt self._thermal_properties.plot(plt) return plt def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) # Thermal displacement def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, temperatures=None, direction=None, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacements = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacements\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) if temperatures is None: td.set_temperature_range(t_min, t_max, t_step) else: td.set_temperatures(temperatures) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) td.run() self._thermal_displacements = td return True def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): import matplotlib.pyplot as plt self._thermal_displacements.plot(plt, is_legend=is_legend) return plt def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() # Thermal displacement matrix def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None, t_cif=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacement_matrices = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacement_matrices\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency, lattice=self._primitive.get_cell().T) if t_cif is None: tdm.set_temperature_range(t_min, t_max, t_step) else: tdm.set_temperatures([t_cif]) tdm.run() self._thermal_displacement_matrices = tdm return True def get_thermal_displacement_matrices(self): tdm = self._thermal_displacement_matrices if tdm is not None: return tdm.get_thermal_displacement_matrices() def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def write_thermal_displacement_matrix_to_cif(self, temperature_index): self._thermal_displacement_matrices.write_cif(self._primitive, temperature_index) # Mean square distance between a pair of atoms def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) td.run(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() # Sampling at q-points def set_qpoints_phonon(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._qpoints_phonon = None return False self._qpoints_phonon = QpointsPhonon( np.reshape(q_points, (-1, 3)), self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) return True def get_qpoints_phonon(self): return (self._qpoints_phonon.get_frequencies(), self._qpoints_phonon.get_eigenvectors()) def write_hdf5_qpoints_phonon(self): self._qpoints_phonon.write_hdf5() def write_yaml_qpoints_phonon(self): self._qpoints_phonon.write_yaml() # Normal mode animation def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return False if q_point is None: animation = Animation([0, 0, 0], self._dynamical_matrix, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index is None or amplitude is None or num_div is None: print("Warning: Parameters are not correctly set for " "animation.") return False if anime_type == 'arc' or anime_type is None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) return True # Atomic modulation of normal mode def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._modulation = None return False self._modulation = Modulation(self._dynamical_matrix, dimension, phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() return True def get_modulated_supercells(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulated_supercells() def get_modulations_and_supercell(self): """Return modulations and supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_modulations_and_supercell() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() # Irreducible representation def set_irreps(self, q, is_little_cogroup=False, nac_q_direction=None, degeneracy_tolerance=1e-4): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._irreps = None return None self._irreps = IrReps( self._dynamical_matrix, q, is_little_cogroup=is_little_cogroup, nac_q_direction=nac_q_direction, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) # Group velocity def set_group_velocity(self, q_length=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._group_velocity = None return False self._group_velocity = GroupVelocity( self._dynamical_matrix, q_length=q_length, symmetry=self._primitive_symmetry, frequency_factor_to_THz=self._factor) return True def get_group_velocity(self): return self._group_velocity.get_group_velocity() def get_group_velocity_at_q(self, q_point): if self._group_velocity is None: self.set_group_velocity() self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] # Moment def set_moment(self, order=1, is_projection=False, freq_min=None, freq_max=None): if self._mesh is None: print("Warning: set_mesh has to be done before set_moment") return False else: if is_projection: if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors()) else: moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights()) if freq_min is not None or freq_max is not None: moment.set_frequency_range(freq_min=freq_min, freq_max=freq_max) moment.run(order=order) self._moment = moment.get_moment() return True def get_moment(self): return self._moment ################# # Local methods # ################# def _run_force_constants_from_forces(self, distributed_atom_list=None, decimals=None, computation_algorithm="svd"): if self._displacement_dataset is not None: self._force_constants = get_fc2( self._supercell, self._symmetry, self._displacement_dataset, atom_list=distributed_atom_list, decimals=decimals, computation_algorithm=computation_algorithm) def _set_dynamical_matrix(self): self._dynamical_matrix = None if (self._supercell is None or self._primitive is None): print("Bug: Supercell or primitive is not created.") return False elif self._force_constants is None: print("Warning: Force constants are not prepared.") return False elif self._primitive.get_masses() is None: print("Warning: Atomic masses are not correctly set.") return False else: if self._nac_params is None: self._dynamical_matrix = DynamicalMatrix( self._supercell, self._primitive, self._force_constants, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) else: self._dynamical_matrix = DynamicalMatrixNAC( self._supercell, self._primitive, self._force_constants, nac_params=self._nac_params, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) return True def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: Point group symmetries of supercell and primitive" "cell are different.") def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_supercells_with_displacements(self): supercells = [] for disp in self._displacement_dataset['first_atoms']: positions = self._supercell.get_positions() positions[disp['number']] += disp['displacement'] supercells.append(Atoms( numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) if self._primitive_matrix is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix) self._primitive = get_primitive( self._supercell, trans_mat, self._symprec) num_satom = self._supercell.get_number_of_atoms() num_patom = self._primitive.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False
def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=None, factor=VaspToTHz, is_auto_displacements=None, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): if is_auto_displacements is not None: print("Warning: \'is_auto_displacements\' argument is obsolete.") if is_auto_displacements is False: print("Sets of displacements are not created as default.") else: print("Use \'generate_displacements\' method explicitly to " "create sets of displacements.") if distance is not None: print("Warning: \'distance\' keyword argument is obsolete at " "Phonopy instantiation.") print("Specify \'distance\' keyword argument when calling " "\'generate_displacements\'") print("method (See the Phonopy API document).") self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = Atoms(atoms=unitcell) self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None