def obtain_eigenvectors_from_phonopy(structure, q_vector, NAC=False): # Checking data force_atoms_file = structure.get_force_set().item(0)["natom"] force_atoms_input = np.product(np.diagonal(structure.get_super_cell_phonon())) * structure.get_number_of_atoms() if force_atoms_file != force_atoms_input: print("Error: FORCE_SETS file does not match with SUPERCELL MATRIX") exit() # 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, structure.get_super_cell_phonon(), primitive_matrix=structure.get_primitive_matrix(), is_auto_displacements=False, ) # 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) phonon.set_displacement_dataset(copy.deepcopy(structure.get_force_set())) phonon.produce_force_constants() frequencies, eigenvectors = phonon.get_frequencies_with_eigenvectors(q_vector) # Making sure eigenvectors are orthonormal (can be omitted) if True: eigenvectors = eigenvectors_normalization(eigenvectors) print("Testing eigenvectors orthonormality") np.set_printoptions(precision=3, suppress=True) print(np.dot(eigenvectors.T, np.ma.conjugate(eigenvectors)).real) np.set_printoptions(suppress=False) # Arranging eigenvectors by atoms and dimensions number_of_dimensions = structure.get_number_of_dimensions() number_of_primitive_atoms = structure.get_number_of_primitive_atoms() arranged_ev = np.array( [ [ [eigenvectors[j * number_of_dimensions + k, i] for k in range(number_of_dimensions)] for j in range(number_of_primitive_atoms) ] for i in range(number_of_primitive_atoms * number_of_dimensions) ] ) return arranged_ev, frequencies
def _get_phonon(self): cell = read_vasp(os.path.join(data_dir, "..", "POSCAR_NaCl")) phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) filename = os.path.join(data_dir, "..", "FORCE_SETS_NaCl") force_sets = parse_FORCE_SETS(filename=filename) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() filename_born = os.path.join(data_dir, "..", "BORN_NaCl") nac_params = parse_BORN(phonon.get_primitive(), filename=filename_born) phonon.set_nac_params(nac_params) return phonon
def phonopy_commensurate_shifts_inline(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy from phonopy.harmonic.dynmat_to_fc import get_commensurate_points, DynmatToForceConstants structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() force_constants = kwargs.pop('force_constants').get_array('force_constants') r_force_constants = kwargs.pop('r_force_constants').get_array('force_constants') # 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'], distance=phonopy_input['distance']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() phonon.set_force_constants(force_constants) dynmat2fc = DynmatToForceConstants(primitive, supercell) com_points = dynmat2fc.get_commensurate_points() phonon.set_qpoints_phonon(com_points, is_eigenvectors=True) frequencies_h = phonon.get_qpoints_phonon()[0] phonon.set_force_constants(r_force_constants) phonon.set_qpoints_phonon(com_points, is_eigenvectors=True) frequencies_r = phonon.get_qpoints_phonon()[0] shifts = frequencies_r - frequencies_h # Stores DOS data in DB as a workflow result commensurate = ArrayData() commensurate.set_array('qpoints', com_points) commensurate.set_array('shifts', shifts) return {'commensurate': commensurate}
def get_rand_FCM(self, asum=15, force=10): """ Generate a symmeterized force constant matrix from an unsymmeterized matrix that has no unstable modes and also obeys the acoustic sum rule through an iterative procedure Args: force (float): maximum force constant asum (int): number of iterations to attempt to obey the acoustic sum rule Return: NxNx3x3 np.array representing the force constant matrix """ numsites = len(self.structure.sites) structure = pymatgen.io.phonopy.get_phonopy_structure(self.structure) pnstruc = Phonopy(structure, np.eye(3), np.eye(3)) dyn = self.get_unstable_FCM(force) dyn = self.get_stable_FCM(dyn) dyn = np.reshape(dyn, (numsites, 3, numsites, 3)).swapaxes(1, 2) dynmass = np.zeros([len(self.structure), len(self.structure), 3, 3]) masses = [] for j in range(numsites): masses.append(self.structure.sites[j].specie.atomic_mass) dynmass = np.zeros([numsites, numsites, 3, 3]) for m in range(numsites): for n in range(numsites): dynmass[m][n] = dyn[m][n] * np.sqrt(masses[m]) * np.sqrt( masses[n]) supercell = pnstruc.get_supercell() primitive = pnstruc.get_primitive() converter = dyntofc.DynmatToForceConstants(primitive, supercell) dyn = np.reshape(np.swapaxes(dynmass, 1, 2), (numsites * 3, numsites * 3)) converter.set_dynamical_matrices(dynmat=[dyn]) converter.run() fc = converter.get_force_constants() return fc
def _get_phonon_NaCl(self): cell = read_vasp(os.path.join(data_dir, "..", "POSCAR_NaCl")) phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) filename = os.path.join(data_dir, "..", "FORCE_SETS_NaCl") force_sets = parse_FORCE_SETS(filename=filename) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() filename_born = os.path.join(data_dir, "..", "BORN_NaCl") nac_params = parse_BORN(phonon.get_primitive(), filename=filename_born) phonon.set_nac_params(nac_params) return phonon
def _get_phonon(self, cell): phonon = Phonopy(cell, np.diag([2, 2, 2]), primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) filename = os.path.join(data_dir, "../FORCE_SETS_NaCl") force_sets = parse_FORCE_SETS(filename=filename) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() filename_born = os.path.join(data_dir, "../BORN_NaCl") nac_params = parse_BORN(phonon.get_primitive(), filename=filename_born) nac_params['method'] = 'wang' phonon.set_nac_params(nac_params) return phonon
def get_commensurate(structure, ph_settings): from phonopy import Phonopy from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants from aiida_phonopy.workchains.phonon import phonopy_bulk_from_structure phonon = Phonopy(phonopy_bulk_from_structure(structure), ph_settings.dict.supercell, primitive_matrix=ph_settings.dict.primitive, symprec=ph_settings.dict.symmetry_precision) primitive = phonon.get_primitive() supercell = phonon.get_supercell() dynmat2fc = DynmatToForceConstants(primitive, supercell) commensurate = dynmat2fc.get_commensurate_points() return dynmat2fc, commensurate
def get_phonon(structure, force_constants, ph_settings, nac_data=None): from phonopy import Phonopy from aiida_phonopy.workchains.phonon import phonopy_bulk_from_structure phonon = Phonopy(phonopy_bulk_from_structure(structure), ph_settings.dict.supercell, primitive_matrix=ph_settings.dict.primitive, symprec=ph_settings.dict.symmetry_precision) if force_constants is not None: phonon.set_force_constants(force_constants.get_data()) if nac_data is not None: primitive = phonon.get_primitive() nac_parameters = nac_data.get_born_parameters_phonopy(primitive_cell=primitive.get_cell()) phonon.set_nac_params(nac_parameters) return phonon
def get_initialized_phononopy_instance(initial_structure, phonopy_inputs, force_constants_path=None, born_path=None): """ Initializes and returns a valid Phonopy instance and with displacements internally generated. If force_constants_path is specified, the phonopy instance is initialized with the force constants at the given path. If the born_path is specified, the born file at this path is used to initialize the nac parameters of the phonopy instance, but only if the nac key has a value of true in the phonopy_inputs dictionary. phonopy_inputs should be a dictionary that looks like: phonopy_inputs_dictionary = { 'supercell_dimensions': [2, 2, 2], 'symprec': 0.001, 'displacement_distance': 0.01, 'nac': True ... } """ unit_cell_phonopy_structure = convert_structure_to_phonopy_atoms( initial_structure) supercell_dimensions_matrix = np.diag( phonopy_inputs['supercell_dimensions']) phonon = Phonopy(unitcell=unit_cell_phonopy_structure, supercell_matrix=supercell_dimensions_matrix, symprec=phonopy_inputs['symprec']) phonon.generate_displacements( distance=phonopy_inputs['displacement_distance']) if force_constants_path != None: force_constants = parse_FORCE_CONSTANTS(filename=force_constants_path) phonon.set_force_constants(force_constants) if born_path != None and (phonopy_inputs.has_key('nac') and phonopy_inputs['nac']): nac_params = parse_BORN(phonon.get_primitive(), filename=born_path) phonon.set_nac_params(nac_params) return phonon
def get_primitive(structure, ph_settings): from phonopy import Phonopy phonon = Phonopy( phonopy_atoms_from_structure(structure), supercell_matrix=ph_settings.get_dict()['supercell_matrix'], primitive_matrix=ph_settings.get_dict()['primitive_matrix'], symprec=ph_settings.get_dict()['symmetry_tolerance']) primitive_phonopy = phonon.get_primitive() primitive_cell = primitive_phonopy.get_cell() symbols = primitive_phonopy.get_chemical_symbols() positions = primitive_phonopy.get_positions() primitive_structure = DataFactory('structure')(cell=primitive_cell) for symbol, position in zip(symbols, positions): primitive_structure.append_atom(position=position, symbols=symbol) return {'primitive_structure': primitive_structure}
def get_primitive(structure, ph_settings): from phonopy import Phonopy phonon = Phonopy(phonopy_bulk_from_structure(structure), supercell_matrix=ph_settings.dict.supercell, primitive_matrix=ph_settings.dict.primitive, symprec=ph_settings.dict.symmetry_precision) primitive_phonopy = phonon.get_primitive() primitive_cell = primitive_phonopy.get_cell() symbols = primitive_phonopy.get_chemical_symbols() positions = primitive_phonopy.get_positions() primitive_structure = StructureData(cell=primitive_cell) for symbol, position in zip(symbols, positions): primitive_structure.append_atom(position=position, symbols=symbol) return {'primitive_structure': primitive_structure}
def parse_partial_DOS(filename, structure, parameters): partial_dos = np.loadtxt(filename) from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy 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, supercell_matrix=parameters.dict.supercell, primitive_matrix=parameters.dict.primitive, symprec=parameters.dict.symmetry_precision) dos = PhononDosData(frequencies=partial_dos.T[0], dos=np.sum(partial_dos[:, 1:], axis=1), partial_dos=partial_dos[:, 1:].T, atom_labels=phonon.get_primitive().get_chemical_symbols()) return dos
def get_commensurate_points(structure, phonopy_input): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy.harmonic.dynmat_to_fc import DynmatToForceConstants 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) phonon = Phonopy(bulk, phonopy_input['supercell'], primitive_matrix=phonopy_input['primitive'], distance=phonopy_input['distance'], symprec=phonopy_input['symmetry_precision']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() dynmat2fc = DynmatToForceConstants(primitive, supercell) com_points = dynmat2fc.get_commensurate_points() return com_points
def obtain_phonon_dispersion_spectra(structure, bands_ranges, NAC=False, band_resolution=30): print("Calculating phonon dispersion spectra...") bulk = PhonopyAtoms( symbols=structure.get_atomic_types(), scaled_positions=structure.get_scaled_positions(), cell=structure.get_cell().T, ) phonon = Phonopy( bulk, structure.get_super_cell_phonon(), primitive_matrix=structure.get_primitive_matrix(), is_auto_displacements=False, ) if NAC: print("Phonopy warning: Using Non Analitical Corrections") print("BORN file is needed to do this") get_is_symmetry = True # sfrom 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) phonon.set_displacement_dataset(copy.deepcopy(structure.get_force_set())) phonon.produce_force_constants() bands = [] for q_start, q_end in bands_ranges: band = [] for i in range(band_resolution + 1): band.append(np.array(q_start) + (np.array(q_end) - np.array(q_start)) / band_resolution * i) bands.append(band) phonon.set_band_structure(bands) return phonon.get_band_structure()
# [0.5, 0.5, 0.5], # [0.5, 0, 0], # [0, 0.5, 0], # [0, 0, 0.5]]) phonon = Phonopy(unitcell, [[2, 0, 0], [0, 2, 0], [0, 0, 2]], primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) symmetry = phonon.get_symmetry() print "Space group:", symmetry.get_international_table() force_sets = parse_FORCE_SETS() phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() primitive = phonon.get_primitive() # Born effective charges and dielectric constants are read from BORN file. nac_params = parse_BORN(primitive, filename="BORN") # Or it can be of course given by hand as follows: # born = [[[1.08703, 0, 0], # [0, 1.08703, 0], # [0, 0, 1.08703]], # [[-1.08672, 0, 0], # [0, -1.08672, 0], # [0, 0, -1.08672]]] # epsilon = [[2.43533967, 0, 0], # [0, 2.43533967, 0], # [0, 0, 2.43533967]] # factors = 14.400 # nac_params = {'born': born,
def BuildForceConstants(lattice_vectors, atom_types, atom_pos, q_pts, freqs, eigs, sc_dim, freq_units = 'thz', atom_mass = None, file_path = r"FORCE_CONSTANTS"): """ Use the Phonopy API to take a set of frequencies and eigenvectors, construct a dynamical matrix, transform to a set of force constants, and write out a Phonopy FORCE_CONSTANTS file. Args: lattice_vectors -- 3x3 matrix containing the lattice vectors atom_types -- list of atomic symbols atom_pos -- list of atomic positions ** in fractional coordinates ** q_pts -- list of q-point coordinates freqs -- n_q sets of Frequencies eigs -- n_q sets of eigenvectors sc_dim -- (equivalent) supercell dimension for preparing force constants freq_units -- units of freqs ('thz' or 'inv_cm' -- default: 'thz') atom_mass -- (optional) list of atomic masses (default: taken from Phonopy internal database) file_path -- path to FORCE_CONSTANTS file to write (default: FORCE_CONSTANTS) """ # Check input. _CheckStructure(lattice_vectors, atom_types, atom_pos, atom_mass) for param in q_pts, freqs, eigs, sc_dim: assert param is not None dim_1, dim_2 = np.shape(q_pts) assert dim_1 > 0 and dim_2 == 3 n_at = len(atom_types) n_q = dim_1 n_b = 3 * n_at dim_1, dim_2 = np.shape(freqs) assert dim_1 == n_q and dim_2 == n_b dim_1, dim_2, dim_3, dim_4 = np.shape(eigs) assert dim_1 == n_q and dim_2 == n_b and dim_3 == n_at and dim_4 == 3 dim_1, = np.shape(sc_dim) assert dim_1 == 3 # Obtain a frequency conversion factor to "VASP units". freq_conv_factor = None if freq_units == 'thz': freq_conv_factor = VaspToTHz elif freq_units == 'inv_cm': freq_conv_factor = VaspToCm if freq_conv_factor is None: raise Exception("Error: Unknown freq_units '{0}'.".format(freq_units)) # Create a Phonopy-format structure. structure = PhonopyAtoms( symbols = atom_types, masses = atom_mass, scaled_positions = atom_pos, cell = lattice_vectors ) # Convert supercell expansion to a supercell matrix. dim_1, dim_2, dim_3 = sc_dim if dim_1 != dim_2 != dim_2 != 1: warnings.warn("The dynamical matrix -> force constants transform has only been tested at the Gamma point; please report issues to the developer.", RuntimeWarning) sc_matrix = [[dim_1, 0, 0], [0, dim_2, 0], [0, 0, dim_3]] # Use the main Phonopy object to obtain the primitive cell and supercell. calculator = Phonopy(structure, sc_matrix) primitive = calculator.get_primitive(); supercell = calculator.get_supercell(); # Use the DynmatToForceConstants object to convert frequencies/eigenvectors -> dynamical matrices -> force constants. dynmat_to_fc = DynmatToForceConstants(primitive, supercell) commensurate_points = dynmat_to_fc.get_commensurate_points() # If an input code does not use crystal symmetry or outputs data at all q-points in a sampling mesh, data may be provided for more q-points than there are commensurate points. # However, for most codes this would be an odd situation -> issue a warning. if len(commensurate_points) != n_q: warnings.warn("The number of entries in the q_pts list does not equal the number of commensurate points expected for the supplied supercell matrix.", RuntimeWarning) # Map commensurate points in Phonopy setup to q-points in input data. map_indices = [] for qx_1, qy_1, qz_1 in commensurate_points: map_index = None for i, (qx_2, qy_2, qz_2) in enumerate(q_pts): if math.fabs(qx_2 - qx_1) < _SymPrec and math.fabs(qy_2 - qy_1) < _SymPrec and math.fabs(qz_2 - qz_1) < _SymPrec: map_index = i break if map_index is None: raise Exception("Error: Expected q = ({0: >6.3f}, {1: >6.3f}, {2: >6.3f}) in the q_pts list (this may be a bug; please report to the developer).".format(qx_1, qy_1, qz_1)) # Sanity check. assert map_index not in map_indices map_indices.append(map_index) # Arrange the frequencies and eigenvectors to the layout required by Phonopy. freq_sets, eig_sets = [], [] for index in map_indices: freq_sets.append( [freq / freq_conv_factor for freq in freqs[index]] ) eig = eigs[index] # Eigenvectors need to be a 3Nx3N matrix in the format: # 1_x -> [ m_1, ..., m_3N ] # 1_y -> [ m_1, ..., m_3N ] # ... # N_z -> [ m_1, ..., m_3N ] eig_rows = [] for i in range(0, n_at): for j in range(0, 3): eig_row = [] for k in range(0, n_b): eig_row.append(eig[k][i][j]) eig_rows.append(eig_row) eig_sets.append(eig_rows) freq_sets = np.array(freq_sets, dtype = np.float64) eig_sets = np.array(eig_sets, dtype = np.complex128) # Use the DynmatToForceConstants object to build the dynamical matrices, reverse transform to the force constants, and write a Phonopy FORCE_CONSTANTS file. dynmat_to_fc.set_dynamical_matrices(freq_sets, eig_sets) dynmat_to_fc.run() write_FORCE_CONSTANTS( dynmat_to_fc.get_force_constants(), filename = file_path )
-1.9721233333333332 0 0 0 -1.9721233333333332 0 0 0 -1.9721233333333332""" # # initial settings # cell = read_vasp_from_strings(poscar_str) phonon = Phonopy(cell, np.diag([2, 2, 2])) force_sets = parse_FORCE_SETS_from_strings(force_sets_str, cell.get_number_of_atoms() * 8) phonon.set_force_sets(force_sets) phonon.set_post_process(primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], is_nac=True) born_params = parse_BORN_from_strings(born_str, phonon.get_primitive()) phonon.set_nac_params(born_params) print phonon.get_symmetry().get_international_table() primitive = phonon.get_primitive() reclat = np.linalg.inv(primitive.get_cell()) print reclat ndiv = 100 band = get_band([0.0, 0.0, 0.0], [0.5, 0.5, 0.0], ndiv) bands = [band] phonon.set_band_structure(bands) # # Run1 #
def get_properties_from_phonopy(**kwargs): """ Calculate DOS and thermal properties using phonopy (locally) :param structure: StructureData Object :param ph_settings: Parametersdata object containing a dictionary with the data needed to run phonopy: supercells matrix, primitive matrix and q-points mesh. :param force_constants: (optional)ForceConstantsData object containing the 2nd order force constants :param force_sets: (optional) ForceSetsData object containing the phonopy force sets :param nac: (optional) ArrayData object from a single point calculation data containing dielectric tensor and Born charges :return: phonon band structure, force constants, thermal properties and DOS """ structure = kwargs.pop('structure') ph_settings = kwargs.pop('ph_settings') bands = kwargs.pop('bands') from phonopy import Phonopy phonon = Phonopy(phonopy_bulk_from_structure(structure), supercell_matrix=ph_settings.dict.supercell, primitive_matrix=ph_settings.dict.primitive, symprec=ph_settings.dict.symmetry_precision) if 'force_constants' in kwargs: force_constants = kwargs.pop('force_constants') phonon.set_force_constants(force_constants.get_data()) else: force_sets = kwargs.pop('force_sets') phonon.set_displacement_dataset(force_sets.get_force_sets()) phonon.produce_force_constants() force_constants = ForceConstantsData(data=phonon.get_force_constants()) if 'nac_data' in kwargs: print('use born charges') nac_data = kwargs.pop('nac_data') primitive = phonon.get_primitive() nac_parameters = nac_data.get_born_parameters_phonopy( primitive_cell=primitive.get_cell()) phonon.set_nac_params(nac_parameters) # Normalization factor primitive to unit cell normalization_factor = phonon.unitcell.get_number_of_atoms( ) / phonon.primitive.get_number_of_atoms() # DOS phonon.set_mesh(ph_settings.dict.mesh, is_eigenvectors=True, is_mesh_symmetry=False) phonon.set_total_DOS(tetrahedron_method=True) phonon.set_partial_DOS(tetrahedron_method=True) total_dos = phonon.get_total_DOS() partial_dos = phonon.get_partial_DOS() dos = PhononDosData( frequencies=total_dos[0], dos=total_dos[1] * normalization_factor, partial_dos=np.array(partial_dos[1]) * normalization_factor, atom_labels=np.array(phonon.primitive.get_chemical_symbols())) # 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('heat_capacity', cv * normalization_factor) # BAND STRUCTURE phonon.set_band_structure(bands.get_bands()) band_structure = BandStructureData(bands=bands.get_bands(), labels=bands.get_labels(), unitcell=bands.get_unitcell()) band_structure.set_band_structure_phonopy(phonon.get_band_structure()) return { 'thermal_properties': thermal_properties, 'dos': dos, 'band_structure': band_structure, 'force_constants': force_constants }
def get_phonon_tb( # phonopy_atoms=[], atoms=[], fc=[], out_file="phonopyTB_hr.dat", distance_to_A=1.0, scell=np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]), factor=VaspToTHz, symprec=1e-05, displacement_distance=0.01, ): """Generate phonon TB Hamiltonia, along with WannierHamn.""" # Forked from Wannier-tools unitcell = atoms.phonopy_converter() # unitcell = phonopy_atoms prim_mat = np.array(PhonopyInputs(atoms).prim_axis().split("=")[1].split(), dtype="float").reshape(3, 3) # print("cell", unitcell.cell) num_atom = unitcell.get_number_of_atoms() num_satom = determinant(scell) * num_atom if fc.shape[0] != num_satom: print("Check Force constant matrix.") phonon = Phonopy( unitcell, scell, primitive_matrix=prim_mat, factor=factor, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=symprec, is_symmetry=True, use_lapack_solver=False, log_level=1, ) supercell = phonon.get_supercell() primitive = phonon.get_primitive() # Set force constants phonon.set_force_constants(fc) phonon._set_dynamical_matrix() dmat = phonon._dynamical_matrix # rescale fcmat by THZ**2 fcmat = dmat._force_constants * factor**2 # FORCE_CONSTANTS # fcmat = dmat._force_constants * factor ** 2 # FORCE_CONSTANTS smallest_vectors = dmat._smallest_vectors # mass = dmat._mass mass = dmat._pcell.get_masses() print("mass=", mass) multi = dmat._multiplicity reduced_bases = get_reduced_bases(supercell.get_cell(), symprec) positions = np.dot(supercell.get_positions(), np.linalg.inv(reduced_bases)) # for pos in positions: pos -= np.rint(pos) relative_scale = np.dot(reduced_bases, np.linalg.inv(primitive.get_cell())) super_pos = np.zeros((num_satom, 3), dtype=np.float64) for i in range(num_satom): super_pos[i] = np.dot(positions[i], relative_scale) p2s_map = dmat._p2s_map = primitive.get_primitive_to_supercell_map() s2p_map = dmat._s2p_map = primitive.get_supercell_to_primitive_map() num_satom = supercell.get_number_of_atoms() num_patom = primitive.get_number_of_atoms() get_phonon_hr( fcmat, smallest_vectors, mass, multi, super_pos, p2s_map, s2p_map, num_satom, num_patom, out_file, ) print("phonopy_TB.dat generated! ")
class AtomicContributionsCalculator: def __init__(self, PoscarName='POSCAR', ForceConstants=False, ForceFileName='FORCE_SETS', BornFileName='BORN', supercell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], nac=False, symprec=1e-5, masses=[], primitive=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], degeneracy_tolerance=1e-4, factor=VaspToCm, q=[0, 0, 0]): """Class that calculates contributions of each atom to the phonon modes at Gamma Args: PoscarName (str): name of the POSCAR that was used for the phonon calculation BornFileName (str): name of the file with BORN charges (formatted with outcar-born) ForceConstants (boolean): If True, ForceConstants are read in. If False, forces are read in. ForceFileName (str): name of the file including force constants or forces supercell (list of lists): reads in supercell nac (boolean): If true, NAC is applied. (please be careful if you give a primitive cell. NAC should then be calculated for primitive cell) symprec (float): contains symprec tag as used in Phonopy masses (list): Masses in this list are used instead of the ones prepared in Phonopy. Useful for isotopes. primitive (list of lists): contains rotational matrix to arrive at primitive cell factor (float): VaspToCm or VaspToTHz or VaspToEv q (list of int): q point for the plot. So far only Gamma works """ self.__unitcell = read_vasp(PoscarName) self.__supercell = supercell self.__phonon = Phonopy(self.__unitcell, supercell_matrix=self.__supercell, primitive_matrix=primitive, factor=factor, symprec=symprec) self.__natoms = self.__phonon.get_primitive().get_number_of_atoms() self.__symbols = self.__phonon.get_primitive().get_chemical_symbols() self.__factor = factor # If different masses are supplied if masses: self.__phonon.set_masses(masses) self.__masses = self.__phonon.get_primitive().get_masses() # Forces or Force Constants if not ForceConstants: self.__set_ForcesSets(filename=ForceFileName, phonon=self.__phonon) if ForceConstants: self.__set_ForceConstants(filename=ForceFileName, phonon=self.__phonon) # Apply NAC Correction if nac: BORN_file = parse_BORN(self.__phonon.get_primitive(), filename=BornFileName) self.__BORN_CHARGES = BORN_file['born'] self.__phonon.set_nac_params(BORN_file) # frequencies and eigenvectors at Gamma self._frequencies, self._eigvecs = self.__phonon.get_frequencies_with_eigenvectors( q) self.__NumberOfBands = len(self._frequencies) # Nicer format of the eigenvector file self.__FormatEigenvectors() # Get Contributions self.__set_Contributions() self.__set_Contributions_withoutmassweight() # irrepsobject try: self.__set_IRLabels(phonon=self.__phonon, degeneracy_tolerance=degeneracy_tolerance, factor=factor, q=q, symprec=symprec) except: print( "Cannot assign IR labels. Play around with symprec, degeneracy_tolerance. The point group could not be implemented." ) self.__freqlist = {} for i in range(0, len(self._frequencies)): self.__freqlist[i] = i def show_primitivecell(self): """ shows primitive cell used for the plots and evaluations on screen """ print(self.__phonon.get_primitive()) def __set_ForcesSets(self, filename, phonon): """ sets forces """ force_sets = parse_FORCE_SETS(filename=filename) phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() def __set_ForceConstants(self, filename, phonon): """ sets force constants """ force_constants = parse_FORCE_CONSTANTS(filename=filename) phonon.set_force_constants(force_constants) def __set_IRLabels(self, phonon, degeneracy_tolerance, factor, q, symprec): """ sets list of irreducible labels and list of frequencies without degeneracy """ # phonon.set_dynamical_matrix() self.__Irrep = IrReps(dynamical_matrix=phonon._dynamical_matrix, q=q, is_little_cogroup=False, nac_q_direction=None, factor=factor, symprec=symprec, degeneracy_tolerance=degeneracy_tolerance) self.__Irrep.run() self._IRLabels = self.__Irrep._get_ir_labels() self.__ListOfModesWithDegeneracy = self.__Irrep._get_degenerate_sets() self.__freqlist = {} for band in range(len(self.__ListOfModesWithDegeneracy)): self.__freqlist[band] = self.__ListOfModesWithDegeneracy[band][0] def __FormatEigenvectors(self): """ Formats eigenvectors to a dictionary: the first argument is the number of bands, the second the number of atoms, the third the Cartesian coordinate """ self._EigFormat = {} for alpha in range(self.__NumberOfBands): laufer = 0 for beta in range(self.__natoms): for xyz in range(0, 3): self._EigFormat[beta, alpha, xyz] = self._eigvecs[laufer][alpha] laufer = laufer + 1 def _Eigenvector(self, atom, band, xoryorz): """ Gives a certain eigenvector corresponding to one specific atom, band and Cartesian coordinate args: atom (int) : number of the atoms (same order as in POSCAR) band (int) : number of the frequency (ordered by energy) xoryorz (int): Cartesian coordinate of the eigenvector """ return np.real(self._EigFormat[atom, band, xoryorz]) def __massEig(self, atom, band, xoryorz): """ Gives a certain eigenvector divided by sqrt(mass of the atom) corresponding to one specific atom, band and Cartesian coordinate args: atom (int) : number of the atoms (same order as in POSCAR) band (int) : number of the frequency (ordered by energy) xoryorz (int): Cartesian coordinate of the eigenvector """ return self._Eigenvector(atom, band, xoryorz) / np.sqrt( self.__masses[atom]) def __set_Contributions(self): """ Calculate contribution of each atom to modes" """ self._PercentageAtom = {} for freq in range(len(self._frequencies)): for atom in range(self.__natoms): sum = 0 for alpha in range(3): sum = sum + abs( self._Eigenvector(atom, freq, alpha) * self._Eigenvector(atom, freq, alpha)) self._PercentageAtom[freq, atom] = sum def __get_Contributions(self, band, atom): """ Gives contribution of specific atom to modes with certain frequency args: band (int): number of the frequency (ordered by energy) """ return self._PercentageAtom[band, atom] def __set_Contributions_withoutmassweight(self): """ Calculate contribution of each atom to modes Here, eigenvectors divided by sqrt(mass of the atom) are used for the calculation """ self.__PercentageAtom_massweight = {} atomssum = {} saver = {} for freq in range(len(self._frequencies)): atomssum[freq] = 0 for atom in range(self.__natoms): sum = 0 for alpha in range(3): sum = sum + abs( self.__massEig(atom, freq, alpha) * self.__massEig(atom, freq, alpha)) atomssum[freq] = atomssum[freq] + sum # Hier muss noch was hin, damit rechnung richtig wird saver[freq, atom] = sum for freq in range(len(self._frequencies)): for atom in range(self.__natoms): self.__PercentageAtom_massweight[ freq, atom] = saver[freq, atom] / atomssum[freq] def __get_Contributions_withoutmassweight(self, band, atom): """ Gives contribution of specific atom to modes with certain frequency Here, eigenvectors divided by sqrt(mass of the atom) are used for the calculation args: band (int): number of the frequency (ordered by energy) """ return self.__PercentageAtom_massweight[band, atom] def write_file(self, filename="Contributions.txt"): """ Writes contributions of each atom in file args: filename (str): filename """ file = open(filename, 'w') file.write('Frequency Contributions \n') for freq in range(len(self._frequencies)): file.write('%s ' % (self._frequencies[freq])) for atom in range(self.__natoms): file.write('%s ' % (self.__get_Contributions(freq, atom))) file.write('\n ') file.close() def plot(self, atomgroups, colorofgroups, legendforgroups, freqstart=[], freqend=[], freqlist=[], labelsforfreq=[], filename="Plot.eps", transmodes=True, massincluded=True): """ Plots contributions of atoms/several atoms to modes with certain frequencies (freqlist starts at 1 here) args: atomgroups (list of list of ints): list that groups atoms, atom numbers start at 1 colorofgroups (list of str): list that matches a color to each group of atoms legendforgroups (list of str): list that gives a legend for each group of atoms freqstart (float): min frequency of plot in cm-1 freqend (float): max frequency of plot in cm-1 freqlist (list of int): list of frequencies that will be plotted; if no list is given all frequencies in the range from freqstart to freqend are plotted, list begins at 1 labelsforfreq (list of str): list of labels (str) for each frequency filename (str): filename for the plot transmodes (boolean): if transmode is true than translational modes are shown massincluded (boolean): if false, uses eigenvector divided by sqrt(mass of the atom) for the calculation instead of the eigenvector """ p = {} summe = {} try: if labelsforfreq == []: labelsforfreq = self._IRLabels except: print("") if freqlist == []: freqlist = self.__freqlist else: for freq in range(len(freqlist)): freqlist[freq] = freqlist[freq] - 1 newfreqlist = [] newlabelsforfreq = [] for freq in range(len(freqlist)): if not transmodes: if not freqlist[freq] in [0, 1, 2]: newfreqlist.append(freqlist[freq]) try: newlabelsforfreq.append(labelsforfreq[freq]) except: newlabelsforfreq.append('') else: newfreqlist.append(freqlist[freq]) try: newlabelsforfreq.append(labelsforfreq[freq]) except: newlabelsforfreq.append('') self._plot(atomgroups=atomgroups, colorofgroups=colorofgroups, legendforgroups=legendforgroups, freqstart=freqstart, freqend=freqend, freqlist=newfreqlist, labelsforfreq=newlabelsforfreq, filename=filename, massincluded=massincluded) def _plot(self, atomgroups, colorofgroups, legendforgroups, freqstart=[], freqend=[], freqlist=[], labelsforfreq=[], filename="Plot.eps", massincluded=True): """ Plots contributions of atoms/several atoms to modes with certain frequencies (freqlist starts at 0 here) args: atomgroups (list of list of ints): list that groups atoms, atom numbers start at 1 colorofgroups (list of str): list that matches a color to each group of atoms legendforgroups (list of str): list that gives a legend for each group of atoms freqstart (float): min frequency of plot in cm-1 freqend (float): max frequency of plot in cm-1 freqlist (list of int): list of frequencies that will be plotted; this freqlist starts at 0 labelsforfreq (list of str): list of labels (str) for each frequency filename (str): filename for the plot massincluded (boolean): if false, uses eigenvector divided by sqrt(mass of the atom) for the calculation instead of the eigenvector """ # setting of some parameters in matplotlib: http://matplotlib.org/users/customizing.html mpl.rcParams["savefig.directory"] = os.chdir(os.getcwd()) mpl.rcParams["savefig.format"] = 'eps' fig, ax1 = plt.subplots() p = {} summe = {} for group in range(len(atomgroups)): color1 = colorofgroups[group] Entry = {} for freq in range(len(freqlist)): Entry[freq] = 0 for number in atomgroups[group]: # set the first atom to 0 atom = int(number) - 1 for freq in range(len(freqlist)): if massincluded: Entry[freq] = Entry[freq] + self.__get_Contributions( freqlist[freq], atom) else: Entry[freq] = Entry[ freq] + self.__get_Contributions_withoutmassweight( freqlist[freq], atom) if group == 0: summe[freq] = 0 # plot bar chart p[group] = ax1.barh(np.arange(len(freqlist)), list(Entry.values()), left=list(summe.values()), color=color1, edgecolor="black", height=1, label=legendforgroups[group]) # needed for "left" in the bar chart plot for freq in range(len(freqlist)): if group == 0: summe[freq] = Entry[freq] else: summe[freq] = summe[freq] + Entry[freq] labeling = {} for freq in range(len(freqlist)): labeling[freq] = round(self._frequencies[freqlist[freq]], 1) # details for the plot plt.rc("font", size=8) ax1.set_yticklabels(list(labeling.values())) ax1.set_yticks(np.arange(0.0, len(self._frequencies) + 0.0)) ax2 = ax1.twinx() ax2.set_yticklabels(labelsforfreq) ax2.set_yticks(np.arange(0.0, len(self._frequencies) + 0.0)) # start and end of the yrange start, end = self.__get_freqbordersforplot(freqstart, freqend, freqlist) ax1.set_ylim(start - 0.5, end - 0.5) ax2.set_ylim(start - 0.5, end - 0.5) ax1.set_xlim(0.0, 1.0) ax1.set_xlabel('Contribution of Atoms to Modes') if self.__factor == VaspToCm: ax1.set_ylabel('Wavenumber (cm$^{-1}$)') elif self.__factor == VaspToTHz: ax1.set_ylabel('Frequency (THz)') elif self.__factor == VaspToEv: ax1.set_ylabel('Frequency (eV)') else: ax1.set_ylabel('Frequency') ax1.legend(bbox_to_anchor=(0, 1.02, 1, 0.2), loc="lower left", mode="expand", borderaxespad=0, ncol=len(atomgroups)) plt.savefig(filename, bbox_inches="tight") plt.show() def __get_freqbordersforplot(self, freqstart, freqend, freqlist): if freqstart == []: start = 0.0 else: for freq in range(len(freqlist)): if self._frequencies[freqlist[freq]] > freqstart: start = freq break else: start = len(freqlist) if freqend == []: end = len(freqlist) else: for freq in range(len(freqlist) - 1, 0, -1): if self._frequencies[freqlist[freq]] < freqend: end = freq + 1 break else: end = len(freqlist) return start, end def plot_irred(self, atomgroups, colorofgroups, legendforgroups, transmodes=False, irreps=[], filename="Plot.eps", freqstart=[], freqend=[], massincluded=True): """ Plots contributions of atoms/several atoms to modes with certain irreducible representations (selected by Mulliken symbol) args: atomgroups (list of list of ints): list that groups atoms, atom numbers start at 1 colorofgroups (list of str): list that matches a color to each group of atoms legendforgroups (list of str): list that gives a legend for each group of atoms transmodes (boolean): translational modes are included if true irreps (list of str): list that includes the irreducible modes that are plotted filename (str): filename for the plot massincluded (boolean): if false, uses eigenvector divided by sqrt(mass of the atom) for the calculation instead of the eigenvector """ freqlist = [] labelsforfreq = [] for band in range(len(self.__freqlist)): if self._IRLabels[band] in irreps: if not transmodes: if not self.__freqlist[band] in [0, 1, 2]: freqlist.append(self.__freqlist[band]) labelsforfreq.append(self._IRLabels[band]) else: freqlist.append(self.__freqlist[band]) labelsforfreq.append(self._IRLabels[band]) self._plot(atomgroups=atomgroups, colorofgroups=colorofgroups, legendforgroups=legendforgroups, filename=filename, freqlist=freqlist, labelsforfreq=labelsforfreq, freqstart=freqstart, freqend=freqend, massincluded=massincluded)
class IR: def __init__(self, PoscarName='POSCAR', BornFileName='BORN', ForceConstants=False, ForceFileName='FORCE_SETS', supercell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]] , nac=False, symprec=1e-5, masses=[], primitive=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], degeneracy_tolerance=1e-5): """ Class for calculating the IR spectra in the dipole approximation according to: P. Giannozzi and S. Baroni, J. Chem. Phys. 100, 8537 (1994). and D. Karhanek, T. Bucko, J. Hafner, J. Phys.: Condens. Matter 22 265006 (2010). This class was also carefully compared to the results of the script by D. Karhanek available at http://homepage.univie.ac.at/david.karhanek/downloads.html args: PoscarNamse (str): name of the POSCAR that was used for the phonon calculation BornFileName (str): name of the file with BORN charges (formatted with outcar-born) ForceConstants (boolean): If True, ForceConstants are read in. If False, forces are read in. ForceFileName (str): name of the file including force constants or forces supercell (list of lists): reads in supercell nac (boolean): If true, NAC is applied. symprec (float): contains symprec tag as used in Phonopy masses (list): Masses in this list are used instead of the ones prepared in Phonopy. Useful for isotopes. primitive (list of lists): contains rotational matrix to arrive at primitive cell degeneracy_tolerance (float): tolerance for degenerate modes """ self.__unitcell = read_vasp(PoscarName) self.__supercell = supercell self.__phonon = Phonopy(self.__unitcell, supercell_matrix=self.__supercell, primitive_matrix=primitive, factor=VaspToCm, symprec=symprec) self.__natoms = self.__phonon.get_primitive().get_number_of_atoms() self._degeneracy_tolerance = degeneracy_tolerance # If different masses are supplied if not ForceConstants: self.__force_sets = parse_FORCE_SETS(filename=ForceFileName) self.__phonon.set_displacement_dataset(self.__force_sets) self.__phonon.produce_force_constants() if ForceConstants: force_constants = parse_FORCE_CONSTANTS(filename=ForceFileName) self.__phonon.set_force_constants(force_constants) if masses: self.__phonon._build_supercell() self.__phonon._build_primitive_cell() # if ForceConstants: # force_constants = parse_FORCE_CONSTANTS(filename=ForceFileName) # self.__phonon.set_force_constants(force_constants) self.__phonon.set_masses(masses) self.__masses = self.__phonon.get_primitive().get_masses() # Forces or Force Constants # Read in BORN file BORN_file = parse_BORN(self.__phonon.get_primitive(), filename=BornFileName) self.__BORN_CHARGES = BORN_file['born'] # Apply NAC Correction if nac: self.__phonon.set_nac_params(BORN_file) self._frequencies, self._eigvecs = self.__phonon.get_frequencies_with_eigenvectors([0, 0, 0]) self.__NumberOfBands = len(self._frequencies) # Nicer format of the eigenvector file self.__FormatEigenvectors() # Get dipole approximation of the intensitiess self.__set_intensities() def __FormatEigenvectors(self): """ Formats eigenvectors to a dictionary: the first argument is the number of bands, the second the number of atoms, the third the Cartesian coordinate """ self._EigFormat = {} for alpha in range(self.__NumberOfBands): laufer = 0 for beta in range(self.__natoms): for xyz in range(0, 3): self._EigFormat[beta, alpha, xyz] = self._eigvecs[laufer][alpha] laufer = laufer + 1 def _Eigenvector(self, atom, band, xoryorz): """ Gives a certain eigenvector corresponding to one specific atom, band and Cartesian coordinate args: atom (int) : number of the atoms (same order as in POSCAR) band (int) : number of the frequency (ordered by energy) xoryorz (int): Cartesian coordinate of the eigenvector """ return np.real(self._EigFormat[atom, band, xoryorz]) def __massEig(self, atom, band, xoryorz): """ Gives a certain eigenvector divided by sqrt(mass of the atom) corresponding to one specific atom, band and Cartesian coordinate args: atom (int) : number of the atoms (same order as in POSCAR) band (int) : number of the frequency (ordered by energy) xoryorz (int): Cartesian coordinate of the eigenvector """ return self._Eigenvector(atom, band, xoryorz) / np.sqrt(self.__masses[atom]) def __set_intensities(self): """ Calculates the oscillator strenghts according to "P. Giannozzi and S. Baroni, J. Chem. Phys. 100, 8537 (1994)." """ Intensity = {} for freq in range(len(self._frequencies)): Intensity[freq] = 0 for alpha in range(3): sum = 0 for l in range(self.__natoms): for beta in range(3): sum = sum + self.__BORN_CHARGES[l, alpha, beta] * self.__massEig(l, freq, beta) Intensity[freq] = Intensity[freq] + np.power(np.absolute(sum), 2) # get degenerate modes freqlist_deg = get_degenerate_sets(self._frequencies, cutoff=self._degeneracy_tolerance) ReformatIntensity = [] for i in Intensity: ReformatIntensity.append(Intensity[i]) # if degenerate modes exist: if (len(freqlist_deg) < len(self._frequencies)): Intensity_deg = {} for sets in range(len(freqlist_deg)): Intensity_deg[sets] = 0 for band in range(len(freqlist_deg[sets])): Intensity_deg[sets] = Intensity_deg[sets] + ReformatIntensity[freqlist_deg[sets][band]] ReformatIntensity = [] for i in range(len(Intensity_deg)): ReformatIntensity.append(Intensity_deg[i]) Freq = [] for band in range(len(freqlist_deg)): Freq.append(self._frequencies[freqlist_deg[band][0]]) self.__frequencies_deg = np.array(Freq) else: self.__frequencies_deg = self._frequencies self.__Intensity = np.array(ReformatIntensity) def get_intensities(self): """ returns calculated oscillator strengths as a numpy array """ return self.__Intensity def get_frequencies(self): """ returns frequencies as a numpy array """ return self.__frequencies_deg def get_spectrum(self): """ returns spectrum as a dict of numpy arrays """ """ Degeneracy should be treated for the Oscillator strengths """ spectrum = {'Frequencies': self.get_frequencies(), 'Intensities': self.get_intensities()} return spectrum # only gaussian broadening so far def get_gaussiansmearedspectrum(self, sigma): """ returns a spectrum with gaussian-smeared intensities args: sigma (float): smearing """ #unsmearedspectrum = self.get_spectrum() frequencies = self.get_frequencies() Intensity = self.get_intensities() rangex = np.linspace(0, np.nanmax(frequencies) + 50, num=int(np.nanmax(frequencies) + 50) * 100) y = np.zeros(int(np.nanmax(frequencies) + 50) * 100) for i in range(len(frequencies)): y = y + self.__gaussiansmearing(rangex, frequencies[i], Intensity[i], sigma) smearedspectrum = {'Frequencies': rangex, 'Intensities': y} return smearedspectrum def __gaussiansmearing(self, rangex, frequency, Intensity, sigma): """ applies gaussian smearing to a range of x values, a certain frequency, a given intensity args: rangex (ndarray): Which values are in your spectrum frequency (float): frequency corresponding to the intensity that will be smeared Intensity (float): Intensity that will be smeared sigma (float): value for the smearing """ y = np.zeros(rangex.size) y = Intensity * np.exp(-np.power((rangex - frequency), 2) / (2 * np.power(sigma, 2))) * np.power( np.sqrt(2 * np.pi) * sigma, -1) return y def write_spectrum(self, filename, type='yaml'): """ writes oscillator strenghts to file args: filename(str): Filename type(str): either txt or yaml """ #TODO: csv spectrum = self.get_spectrum() if type == 'txt': self.__write_file(filename, spectrum) elif type == 'yaml': self.__write_file_yaml(filename, spectrum) def write_gaussiansmearedspectrum(self, filename, sigma, type='txt'): """ writes smeared oscillator strenghts to file args: filename(str): Filename sigma(float): smearing of the spectrum type(str): either txt or yaml """ #TODO csv spectrum = self.get_gaussiansmearedspectrum(sigma) if type == 'txt': self.__write_file(filename, spectrum) elif type == 'yaml': self.__write_file_yaml(filename, spectrum) def __write_file(self, filename, spectrum): """ writes dict for any spectrum into txt file args: filename(str): Filename spectrum (dict): Includes nparray for 'Frequencies' and 'Intensities' """ Freq = np.array(spectrum['Frequencies'].tolist()) Intens = np.array(spectrum['Intensities'].tolist()) file = open(filename, 'w') file.write('Frequency (cm-1) Oscillator Strengths \n') for i in range(len(Freq)): file.write('%s %s \n' % (Freq[i], Intens[i])) file.close() def __write_file_yaml(self, filename, spectrum): """ writes dict for any spectrum into yaml file args: filename(str): Filename spectrum (dict): Includes nparray for 'Frequencies' and 'Intensities' """ Freq = np.array(spectrum['Frequencies'].tolist()) Intens = np.array(spectrum['Intensities'].tolist()) file = open(filename, 'w') file.write('Frequency: \n') for i in range(len(Freq)): file.write('- %s \n' % (Freq[i])) file.write('Oscillator Strengths: \n') for i in range(len(Intens)): file.write('- %s \n' % (Intens[i])) file.close() def plot_spectrum(self, filename): """ Plots frequencies in cm-1 and oscillator strengths args: filename(str): name of the file """ spectrum = self.get_spectrum() plt.stem(spectrum['Frequencies'].tolist(), spectrum['Intensities'].tolist(), markerfmt=' ') plt.xlabel('Wave number (cm$^{-1}$)') plt.ylabel('Oscillator Strengths') plt.savefig(filename) plt.show() def plot_gaussiansmearedspectrum(self, filename, sigma): """ Plots frequencies in cm-1 and smeared oscillator strengths args: filename(str): name of the file sigma(float): smearing """ spectrum = self.get_gaussiansmearedspectrum(sigma) plt.plot(spectrum['Frequencies'].tolist(), spectrum['Intensities'].tolist()) plt.xlabel('Wave number (cm$^{-1}$)') plt.ylabel('Oscillator Strengths') plt.savefig(filename) plt.show()
def ir_intensity_phonopy( run_dir=".", vasprun="vasprun.xml", BornFileName="BORN", PoscarName="POSCAR", ForceConstantsName="FORCE_CONSTANTS", supercell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], nac=True, symprec=1e-5, primitive=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], degeneracy_tolerance=1e-5, vector=[0, 0, 0], smoothen=False, ): """Calculate IR intensity using DFPT and phonopy.""" from phonopy import Phonopy from phonopy.interface.vasp import read_vasp from phonopy.file_IO import ( parse_BORN, parse_FORCE_CONSTANTS, ) import shutil from phonopy.units import VaspToCm # from phonopy.phonon.degeneracy import ( # degenerate_sets as get_degenerate_sets, # ) # adapted from https://github.com/JaGeo/IR # TODO: Make directory indepndent cwd = os.getcwd() print("Directory:", cwd) os.chdir(run_dir) if not os.path.exists(vasprun): shutil.copy2(vasprun, "vasprun.xml") cmd = str("phonopy --fc vasprun.xml") os.system(cmd) born_file = os.path.join(os.getcwd(), BornFileName) cmd = str("phonopy-vasp-born > ") + str(born_file) os.system(cmd) from jarvis.io.vasp.outputs import Vasprun v = Vasprun(vasprun) strt = v.all_structures[0] strt.write_poscar(PoscarName) unitcell = read_vasp(PoscarName) phonon = Phonopy( unitcell, supercell_matrix=supercell, primitive_matrix=primitive, factor=VaspToCm, symprec=symprec, ) natoms = phonon.get_primitive().get_number_of_atoms() force_constants = parse_FORCE_CONSTANTS(filename=ForceConstantsName) phonon.set_force_constants(force_constants) masses = phonon.get_primitive().get_masses() phonon.set_masses(masses) BORN_file = parse_BORN(phonon.get_primitive(), filename=BornFileName) BORN_CHARGES = BORN_file["born"] # print ('born_charges2',BORN_CHARGES) if nac: phonon.set_nac_params(BORN_file) frequencies, eigvecs = phonon.get_frequencies_with_eigenvectors(vector) # frequencies=VaspToTHz*frequencies/VaspToCm # x, y = ir_intensity( # phonon_eigenvectors=np.real(eigvecs), # phonon_eigenvalues=frequencies, # masses=masses, #np.ones(len(masses)), # born_charges=born_charges, # smoothen=smoothen, # ) NumberOfBands = len(frequencies) EigFormat = {} for alpha in range(NumberOfBands): laufer = 0 for beta in range(natoms): for xyz in range(0, 3): EigFormat[beta, alpha, xyz] = eigvecs[laufer][alpha] laufer = laufer + 1 Intensity = {} intensities = [] for freq in range(len(frequencies)): Intensity[freq] = 0 tmp = 0 for alpha in range(3): asum = 0 for n in range(natoms): for beta in range(3): asum = asum + BORN_CHARGES[n, alpha, beta] * np.real( EigFormat[n, freq, beta] ) / np.sqrt(masses[n]) tmp += asum Intensity[freq] = Intensity[freq] + np.power(np.absolute(asum), 2) intensities.append(Intensity[freq]) os.chdir(cwd) return frequencies, intensities
def phonopy_merge(**kwargs): from phonopy.structure.atoms import Atoms as PhonopyAtoms from phonopy import Phonopy from phonopy.units import VaspToTHz from phonopy.harmonic.dynmat_to_fc import get_commensurate_points, DynmatToForceConstants structure = kwargs.pop('structure') phonopy_input = kwargs.pop('phonopy_input').get_dict() harmonic = kwargs.pop('harmonic') renormalized = kwargs.pop('renormalized') eigenvectors = harmonic.get_array('eigenvectors') frequencies = harmonic.get_array('frequencies') shifts = renormalized.get_array('shifts') # 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'], distance=phonopy_input['distance']) primitive = phonon.get_primitive() supercell = phonon.get_supercell() total_frequencies = frequencies + shifts dynmat2fc = DynmatToForceConstants(primitive, supercell) dynmat2fc.set_dynamical_matrices(total_frequencies / VaspToTHz, eigenvectors) dynmat2fc.run() total_force_constants = dynmat2fc.get_force_constants() # Stores DOS data in DB as a workflow result total_data = ArrayData() total_data.set_array('force_constants', total_force_constants) return {'final_results': total_data} # Start script here # Workflow phonon (at given volume) wf = load_workflow(431) parameters = wf.get_parameters() results = wf.get_results() inline_params = {'structure': results['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': results['force_constants']} harmonic = phonopy_commensurate_inline(**inline_params) # At reference volume (at T = 0) wf = load_workflow(432) parameters = wf.get_parameters() results_r = wf.get_results() results_h = wf.get_results() inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': results_h['force_constants'], 'r_force_constants': results_r['r_force_constants']} renormalized = phonopy_commensurate_shifts_inline(**inline_params) inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'harmonic': harmonic, 'renormalized': renormalized} total = phonopy_merge(**inline_params) print total inline_params = {'structure': results_h['final_structure'], 'phonopy_input': parameters['phonopy_input'], 'force_constants': total['force_constants']} results = phonopy_calculation_inline(**inline_params)[1] band = results['band_structure'] # Phonon Band structure plot plot_data(results['band_structure'])
Scells_quippy.append(scell_qp) # calculate forces and convert to phonopy force_sets force_gap_scells = api_q.calc_force_sets_GAP(gp_xml_file, Scells_quippy) #parse force set and calc force constants phonon_scell.set_forces(force_gap_scells) PhonIO.write_FORCE_SETS(phonon_scell.get_displacement_dataset() ) # write forces & displacements to FORCE_SET force_set = PhonIO.parse_FORCE_SETS() # parse force_sets phonon_scell.set_displacement_dataset( force_set) # force_set is a list of forces and displacements if NAC == True: nac_params = PhonIO.get_born_parameters( open("BORN"), phonon_scell.get_primitive(), phonon_scell.get_primitive_symmetry()) if nac_params['factor'] == None: physical_units = get_default_physical_units(interface_mode) nac_params['factor'] = physical_units['nac_factor'] phonon_scell._nac_params = nac_params phonon_scell.produce_force_constants() phonon_scell.symmetrize_force_constants() api_ph.write_ShengBTE_FC2(phonon_scell.get_force_constants(), filename='FORCE_CONSTANTS_2ND') # phonopy 2.7 changed format, ShengBTE won't read, use the file in api_qpv to write. # calc and plot bandstructure bands = api_ph.qpoints_Band_paths(Qpoints, Band_points) phonon_scell.set_band_structure(bands,
phonon = Phonopy(unitcell, [[2, 0, 0], [0, 2, 0], [0, 0, 2]], primitive_matrix=[[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) symmetry = phonon.get_symmetry() print "Space group:", symmetry.get_international_table() force_sets = parse_FORCE_SETS() phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() primitive = phonon.get_primitive() # Born effective charges and dielectric constants are read from BORN file. nac_params = parse_BORN(primitive, filename="BORN") # Or it can be of course given by hand as follows: # born = [[[1.08703, 0, 0], # [0, 1.08703, 0], # [0, 0, 1.08703]], # [[-1.08672, 0, 0], # [0, -1.08672, 0], # [0, 0, -1.08672]]] # epsilon = [[2.43533967, 0, 0], # [0, 2.43533967, 0], # [0, 0, 2.43533967]] # factors = 14.400 # nac_params = {'born': born,
print "Space group:", symmetry.get_international_table() # Read and convert forces and displacements force_sets = parse_FORCE_SETS(cell.get_number_of_atoms() * 4) # Sets of forces have to be set before phonon.set_post_process or # at phonon.set_post_process(..., sets_of_forces=sets_of_forces, ...). phonon.set_force_sets(force_sets) # To activate non-analytical term correction. phonon.set_post_process(primitive_matrix=[[2./3, -1./3, -1./3], [1./3, 1./3, -2./3], [1./3, 1./3, 1./3]]) # Parameters for non-analytical term correction can be set # also after phonon.set_post_process born = parse_BORN(phonon.get_primitive()) phonon.set_nac_params(born) # Example to obtain dynamical matrix dmat = phonon.get_dynamical_matrix_at_q([0,0,0]) print dmat # Example of band structure calculation bands = [] q_start = np.array([1./3, 1./3, 0]) q_end = np.array([0, 0, 0]) band = [] for i in range(51): band.append(q_start + (q_end - q_start) / 50 * i) bands.append(band)
# Initialize phonon. Supercell matrix has to have the shape of (3, 3) phonon = Phonopy(cell, np.diag([2, 2, 1]), primitive_matrix=[[2. / 3, -1. / 3, -1. / 3], [1. / 3, 1. / 3, -2. / 3], [1. / 3, 1. / 3, 1. / 3]]) symmetry = phonon.get_symmetry() print "Space group:", symmetry.get_international_table() force_sets = parse_FORCE_SETS() phonon.set_displacement_dataset(force_sets) phonon.produce_force_constants() born = parse_BORN(phonon.get_primitive()) phonon.set_nac_params(born) # Example to obtain dynamical matrix dmat = phonon.get_dynamical_matrix_at_q([0, 0, 0]) print dmat # Example of band structure calculation bands = [] q_start = np.array([1. / 3, 1. / 3, 0]) q_end = np.array([0, 0, 0]) band = [] for i in range(51): band.append(q_start + (q_end - q_start) / 50 * i) bands.append(band)
class thermal_conductivity(): def __init__(self, primitive_cell, super_cell): self.primitive_cell = primitive_cell self.super_cell = super_cell def input_files(self, path_POSCAR, path_FORCECONSTANT): bulk = read_vasp(path_POSCAR) self.phonon = Phonopy(bulk, self.super_cell, primitive_matrix=self.primitive_cell) force_constants = file_IO.parse_FORCE_CONSTANTS(path_FORCECONSTANT) self.phonon.set_force_constants(force_constants) return self.phonon def set_mesh(self, mesh): self.phonon.set_mesh(mesh, is_eigenvectors=True) def set_point_defect_parameter(self, fi, defect_mass, defect_fc_ratio): self.mass = self.phonon.get_primitive().get_masses().sum( ) / self.phonon.get_primitive().get_number_of_atoms() self.cellvol = self.phonon.get_primitive().get_volume() self.mass_fc_factor = self.cellvol * 10**-30 / 2.0 / 4 / np.pi * ( fi * ((self.mass - defect_mass) / self.mass + 2 * defect_fc_ratio)**2) def kappa_separate(self, gamma, thetaD, T, P=1, m=3, n=1): """ nkpts: number of phonon k-points frequencies: list of eigenvalue-arrays as output by phonopy vgroup: group velocities T: temperature in K following parameters from Eq (70) in Tritt book, p.14 P: proportionality factor of Umklapp scattering rate, defualt 1 mass: mass in amu (average mass of unitcell) gamma: Gruneisen parameter, unitless thetaD: Debye temperature of relevance (acoustic modes) 283K for Mo3Sb7 m = 3 by default n = 1 by default cellvol: unit cell volume in A^3 """ T = float(T) #self.phonon.set_mesh(mesh, is_eigenvectors=True) #self.mass = self.phonon.get_primitive().get_masses().sum()/self.phonon.get_primitive().get_number_of_atoms() self.cellvol = self.phonon.get_primitive().get_volume() self.qpoints, self.weigths, self.frequencies, self.eigvecs = self.phonon.get_mesh( ) nkpts = self.frequencies.shape[0] numbranches = self.frequencies.shape[1] self.group_velocity = np.zeros((nkpts, numbranches, 3)) for i in range(len(self.qpoints)): self.group_velocity[i, :, :] = self.phonon.get_group_velocity_at_q( self.qpoints[i]) inv_nor_Um = np.zeros((nkpts, numbranches)) inv_point_defect_mass_fc = np.zeros((nkpts, numbranches)) inv_tau_total = np.zeros((nkpts, numbranches)) kappa_Um = np.zeros((nkpts, numbranches)) kappa_point_defect_mass_fc = np.zeros((nkpts, numbranches)) kappa_total = np.zeros((nkpts, numbranches)) self.kappaten = np.zeros((nkpts, numbranches, 3, 3)) v_group = np.zeros((nkpts, numbranches)) capacity = np.zeros((nkpts, numbranches)) for i in range(nkpts): for j in range(numbranches): nu = self.frequencies[i][ j] # frequency (in Thz) of current mode velocity = self.group_velocity[ i, j, :] # cartesian vector of group velocity v_group = (velocity[0]**2 + velocity[1]**2 + velocity[2]**2)**(0.5) * THztoA capacity[i, j] = mode_cv( T, nu * THzToEv) * eV2Joule / (self.cellvol * Ang2meter**3) # tau inverse phonon-phonon, inverse lifetime inv_nor_Um[i, j] = P * hbar * gamma**2 / ( self.mass * amu) / v_group**2 * (T**n) / thetaD * ( 2 * np.pi * THz * nu)**2 * np.exp( (-1.0) * thetaD / m / T) # tau inverse point defect, inverse lifetime inv_point_defect_mass_fc[i, j] = (self.mass_fc_factor) * ( 2 * np.pi * THz * nu)**4 / v_group**3 # tau inverst, inverse lifetime inv_tau_total[ i, j] = inv_nor_Um[i, j] + inv_point_defect_mass_fc[i, j] kappa_total[ i, j] = (1.0 / 3.0) * self.weigths[i] * v_group**2 * capacity[ i, j] / inv_tau_total[i, j] # 1/3 is for isotropic condition kappa_Um[ i, j] = (1.0 / 3.0) * self.weigths[i] * v_group**2 * capacity[ i, j] / inv_nor_Um[i, j] # 1/3 is for isotropic condition kappa_point_defect_mass_fc[ i, j] = (1.0 / 3.0) * self.weigths[i] * v_group**2 * capacity[ i, j] / inv_point_defect_mass_fc[ i, j] # 1/3 is for isotropic condition self.kappaten[i, j, :, :] = self.weigths[i] * np.outer( velocity, velocity) * THztoA**2 * capacity[i, j] / inv_tau_total[i, j] #print(np.sum(inv_nor_Um, axis =1)) #print(np.sum(inv_point_defect_mass_fc, axis =1)) #print(np.sum(inv_tau_total, axis =1)) self.tau = np.concatenate( (inv_nor_Um, inv_point_defect_mass_fc, inv_tau_total), axis=1) self.kappa = [ kappa_Um.sum() / self.weigths.sum(), kappa_point_defect_mass_fc.sum() / self.weigths.sum(), kappa_total.sum() / self.weigths.sum() ] return self.tau, self.kappa, self.kappaten