def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None, solve_dynamical_matrices=True): if self._mesh_numbers is None: print("'mesh' has to be set in Phono3py instantiation.") raise RuntimeError self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh_numbers, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, frequency_scale_factor=frequency_scale_factor, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3q=self._symmetrize_fc3q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, solve_dynamical_matrices=solve_dynamical_matrices, verbose=self._log_level)
def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None): self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, frequency_scale_factor=frequency_scale_factor)
def _get_irt(ph3: Phono3py, mesh, nac_params=None, solve_dynamical_matrices=True): ph3.mesh_numbers = mesh itr = Interaction(ph3.primitive, ph3.grid, ph3.primitive_symmetry, ph3.fc3, cutoff_frequency=1e-4) if nac_params is None: itr.init_dynamical_matrix( ph3.fc2, ph3.phonon_supercell, ph3.phonon_primitive, ) else: itr.init_dynamical_matrix( ph3.fc2, ph3.phonon_supercell, ph3.phonon_primitive, nac_params=nac_params, ) if solve_dynamical_matrices: itr.run_phonon_solver() return itr
class Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3_q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3_q = symmetrize_fc3_q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None # Linewidth (Imaginary part of self energy x 2) at temperatures self._linewidth = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._mesh = np.array(mesh, dtype='intc') self.set_band_indices(band_indices) def set_band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack(self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None): self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, frequency_scale_factor=frequency_scale_factor) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def write_phonons(self, filename=None): if self._interaction is not None: grid_address = self._interaction.get_grid_address() grid_points = np.arange(len(grid_address), dtype='intc') self._interaction.set_phonons(grid_points) freqs, eigvecs, _ = self._interaction.get_phonons() hdf5_filename = write_phonon_to_hdf5(freqs, eigvecs, grid_address, self._mesh, filename=filename) return hdf5_filename else: return False def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, is_translational_symmetry=False, is_permutation_symmetry=False, translational_symmetry_type=None, use_alm=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc2 as get_fc2_alm self._fc2 = get_fc2_alm(self._phonon_supercell, forces_fc2, disp_dataset, self._phonon_supercell_symmetry) else: for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(self._fc2) if is_translational_symmetry: tsym_type = 1 else: tsym_type = 0 if translational_symmetry_type: tsym_type = translational_symmetry_type if tsym_type: set_translational_invariance( self._fc2, translational_symmetry_type=tsym_type) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero is_translational_symmetry=False, is_permutation_symmetry=False, is_permutation_symmetry_fc2=False, translational_symmetry_type=None, use_alm=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc3 as get_fc3_alm fc2, fc3 = get_fc3_alm(self._supercell, forces_fc3, disp_dataset, self._symmetry) else: fc2, fc3 = self._get_fc3(forces_fc3, disp_dataset, cutoff_distance, is_translational_symmetry, is_permutation_symmetry, is_permutation_symmetry_fc2, translational_symmetry_type) # Set fc2 and fc3 self._fc3 = fc3 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero(_fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self, translational_symmetry_type=1): if self._fc2 is not None: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) if self._fc3 is not None: set_translational_invariance_fc3( self._fc3, translational_symmetry_type=translational_symmetry_type) def get_version(self): return __version__ def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 def get_nac_params(self): return self._nac_params def get_primitive(self): return self._primitive def get_unitcell(self): return self._unitcell def get_supercell(self): return self._supercell def get_phonon_supercell(self): return self._phonon_supercell def get_phonon_primitive(self): return self._phonon_primitive def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def get_supercell_matrix(self): return self._supercell_matrix def get_primitive_matrix(self): return self._primitive_matrix def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset def get_displacement_dataset(self): return self._displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_linewidth(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), write_gamma_detail=False): if self._interaction is None: self.set_phph_interaction() self._grid_points = grid_points self._temperatures = temperatures self._linewidth = get_linewidth(self._interaction, grid_points, self._sigmas, temperatures=temperatures, write_detail=write_gamma_detail, log_level=self._log_level) def write_linewidth(self, filename=None): write_linewidth(self._linewidth, self._band_indices, self._mesh, self._grid_points, self._sigmas, self._temperatures, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_gamma_detail=write_gamma_detail, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), output_filename=None): if self._interaction is None: self.set_phph_interaction() if epsilons is None: epsilons = [0.1] self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, self._sigmas, temperatures, output_filename=output_filename, log_level=self._log_level) 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 _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) 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 """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if self._primitive is not None: if (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any(): print("********************* Warning *********************") print(" Primitive cells for fc2 and fc3 can be different.") print("********************* Warning *********************") def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) 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._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[ [p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map()]] self._phonon_supercell.set_masses(s_masses) def _get_fc3(self, forces_fc3, disp_dataset, cutoff_distance, is_translational_symmetry, is_permutation_symmetry, is_permutation_symmetry_fc2, translational_symmetry_type): for forces, disp1 in zip(forces_fc3, disp_dataset['first_atoms']): disp1['forces'] = forces fc2 = get_fc2(self._supercell, self._symmetry, disp_dataset) if is_permutation_symmetry_fc2: set_permutation_symmetry(fc2) if is_translational_symmetry: tsym_type = 1 else: tsym_type = 0 if translational_symmetry_type: tsym_type = translational_symmetry_type if tsym_type: set_translational_invariance( fc2, translational_symmetry_type=tsym_type) count = len(disp_dataset['first_atoms']) for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 fc3 = get_fc3( self._supercell, disp_dataset, fc2, self._symmetry, translational_symmetry_type=tsym_type, is_permutation_symmetry=is_permutation_symmetry, verbose=self._log_level) # Set fc3 elements zero beyond cutoff_distance if cutoff_distance: if self._log_level: print("Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance) self.cutoff_fc3_by_zero(cutoff_distance, fc3=fc3) return fc2, fc3
class Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3q = symmetrize_fc3q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix if type(primitive_matrix) is str and primitive_matrix == 'auto': self._primitive_matrix = self._guess_primitive_matrix() else: self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh_numbers = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._set_mesh_numbers(mesh) self.set_band_indices(band_indices) @property def thermal_conductivity(self): return self._thermal_conductivity def get_thermal_conductivity(self): return self.thermal_conductivity @property def displacements(self): """Return displacements Returns ------- displacements : ndarray Displacements of all atoms of all supercells in Cartesian coordinates. shape=(supercells, natom, 3), dtype='double', order='C' """ dataset = self._displacement_dataset if 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) for disp1 in dataset['first_atoms']: num_scells += len(disp1['second_atoms']) displacements = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') i = 0 for disp1 in dataset['first_atoms']: displacements[i, disp1['number']] = disp1['displacement'] i += 1 for disp1 in dataset['first_atoms']: for disp2 in dataset['second_atoms']: displacements[i, disp2['number']] = disp2['displacement'] i += 1 elif 'forces' in dataset or 'displacements' in dataset: displacements = dataset['displacements'] else: raise RuntimeError("displacement dataset has wrong format.") return displacements @displacements.setter def displacements(self, displacements): """Set displacemens Parameters ---------- displacemens : array_like Atomic displacements of all atoms of all supercells. Only type2 displacement dataset is supported, i.e., displacements has to have the array shape of (supercells, natom, 3). When displacement dataset is in type1, the type2 dataset is created and force information is lost. """ dataset = self._displacement_dataset disps = np.array(displacements, dtype='double', order='C') natom = self._supercell.get_number_of_atoms() if (disps.ndim != 3 or disps.shape[1:] != (natom, 3)): raise RuntimeError("Array shape of displacements is incorrect.") if 'first_atoms' in dataset: dataset = {'displacements': disps} elif 'displacements' in dataset or 'forces' in dataset: dataset['displacements'] = disps @property def forces(self): dataset = self._displacement_dataset if 'forces' in dataset: return dataset['forces'] elif 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) for disp1 in dataset['first_atoms']: num_scells += len(disp1['second_atoms']) forces = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') i = 0 for disp1 in dataset['first_atoms']: forces[i] = disp1['forces'] i += 1 for disp1 in dataset['first_atoms']: for disp2 in disp1['second_atoms']: forces[i] = disp2['forces'] i += 1 return forces else: raise RuntimeError("displacement dataset has wrong format.") @forces.setter def forces(self, forces_fc3): """Set forces in displacement dataset. Parameters ---------- forces_fc3 : array_like A set of atomic forces in displaced supercells. The order of displaced supercells has to match with that in displacement dataset. shape=(displaced supercells, atoms in supercell, 3) """ forces = np.array(forces_fc3, dtype='double', order='C') dataset = self._displacement_dataset if 'first_atoms' in dataset: i = 0 for disp1 in dataset['first_atoms']: disp1['forces'] = forces[i] i += 1 for disp1 in dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['forces'] = forces[i] i += 1 elif 'displacements' in dataset or 'forces' in dataset: dataset['forces'] = forces @property def phonon_displacements(self): """Return displacements for harmonic phonons Returns ------- displacements : ndarray Displacements of all atoms of all supercells in Cartesian coordinates. shape=(supercells, natom, 3), dtype='double', order='C' """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset if 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) natom = self._phonon_supercells.get_number_of_atoms() displacements = np.zeros((num_scells, natom, 3), dtype='double', order='C') for i, disp1 in enumerate(dataset['first_atoms']): displacements[i, disp1['number']] = disp1['displacement'] elif 'forces' in dataset or 'displacements' in dataset: displacements = dataset['displacements'] else: raise RuntimeError("displacement dataset has wrong format.") return displacements @phonon_displacements.setter def phonno_displacements(self, displacements): """Set displacemens Parameters ---------- displacemens : array_like Atomic displacements of all atoms of all supercells. Only type2 displacement dataset is supported, i.e., displacements has to have the array shape of (supercells, natom, 3). When displacement dataset is in type1, the type2 dataset is created and force information is lost. """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset disps = np.array(displacements, dtype='double', order='C') natom = self._phonon_supercell.get_number_of_atoms() if (disps.ndim != 3 or disps.shape[1:] != (natom, 3)): raise RuntimeError("Array shape of displacements is incorrect.") if 'first_atoms' in dataset: dataset = {'displacements': disps} elif 'displacements' in dataset or 'forces' in dataset: dataset['displacements'] = disps @property def phonon_forces(self): if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset if 'forces' in dataset: return dataset['forces'] elif 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) forces = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') for i, disp1 in enumerate(dataset['first_atoms']): forces[i] = disp1['forces'] return forces else: raise RuntimeError("displacement dataset has wrong format.") @phonon_forces.setter def phonon_forces(self, forces_fc2): """Set forces in displacement dataset. Parameters ---------- forces_fc2 : array_like A set of atomic forces in displaced supercells. The order of displaced supercells has to match with that in displacement dataset. shape=(displaced supercells, atoms in supercell, 3) """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") forces = np.array(forces_fc2, dtype='double', order='C') dataset = self._phonon_displacement_dataset if 'first_atoms' in dataset: i = 0 for i, disp1 in enumerate(dataset['first_atoms']): disp1['forces'] = forces[i] i += 1 elif 'displacements' in dataset or 'forces' in dataset: dataset['forces'] = forces @property def dataset(self): return self._displacement_dataset @dataset.setter def dataset(self, dataset): self._displacement_dataset = dataset @property def phonon_dataset(self): return self._phonon_displacement_dataset @phonon_dataset.setter def phonon_dataset(self, dataset): self._phonon_displacement_dataset = dataset @property def band_indices(self): return self._band_indices @band_indices.setter def band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack( self._band_indices).astype('intc') def set_band_indices(self, band_indices): self.band_indices = band_indices def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None, solve_dynamical_matrices=True): if self._mesh_numbers is None: print("'mesh' has to be set in Phono3py instantiation.") raise RuntimeError self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh_numbers, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, frequency_scale_factor=frequency_scale_factor, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3q=self._symmetrize_fc3q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, solve_dynamical_matrices=solve_dynamical_matrices, verbose=self._log_level) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def get_phonon_data(self): if self._interaction is not None: grid_address = self._interaction.get_grid_address() freqs, eigvecs, _ = self._interaction.get_phonons() return freqs, eigvecs, grid_address else: msg = "set_phph_interaction has to be done." raise RuntimeError(msg) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: # 'is_diagonal=False' below is made intentionally. For # third-order force constants, we need better accuracy, # and I expect this choice is better for it, but not very # sure. # In phono3py, two atoms are displaced for each # configuration and the displacements are chosen, first # displacement from the perfect supercell, then second # displacement, considering symmetry. If I choose # is_diagonal=False for the first displacement, the # symmetry is less broken and the number of second # displacements can be smaller than in the case of # is_diagonal=True for the first displacement. This is # done in the call get_least_displacements() in # phonon3.displacement_fc3.get_third_order_displacements(). # # The call get_least_displacements() is only for the # second order force constants, but 'is_diagonal=False' to # be consistent with the above function call, and also for # the accuracy when calculating ph-ph interaction # strength because displacement directions are better to be # close to perpendicular each other to fit force constants. # # On the discussion of the accuracy, these are just my # expectation when I designed phono3py in the early time, # and in fact now I guess not very different. If these are # little different, then I should not surprise users to # change the default behaviour. At this moment, this is # open question and we will have more advance and should # have better specificy external software on this. phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = directions_to_displacement_dataset( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2=None, displacement_dataset=None, symmetrize_fc2=False, is_compact_fc=False, fc_calculator=None, fc_calculator_options=None): """Calculate fc2 from displacements and forces Parameters ---------- forces_fc2 : Dummy argument displacement_dataset : dict Displacements in supercells. There are two types of formats. Type 1. Two atomic displacement in each supercell: {'natom': number of atoms in supercell, 'first_atoms': [ {'number': atom index of first displaced atom, 'displacement': displacement in Cartesian coordinates, 'forces': forces on atoms in supercell, 'second_atoms': [ {'number': atom index of second displaced atom, 'displacement': displacement in Cartesian coordinates}, 'forces': forces on atoms in supercell, ... ] }, ... ] } Type 2. All atomic displacements in each supercell: {'displacements': ndarray, dtype='double', order='C', shape=(supercells, atoms in supercell, 3) 'forces': ndarray, dtype='double',, order='C', shape=(supercells, atoms in supercell, 3)} In type 2, displacements and forces can be given by numpy array with different shape but that can be reshaped to (supercells, natom, 3). symmetrize_fc2 : bool Only for type 1 displacement_dataset, translational and permutation symmetries are applied after creating fc3. This symmetrization is not very sophisticated and can break space group symmetry, but often useful. If better symmetrization is expected, it is recommended to use external force constants calculator such as ALM. Default is False. is_compact_fc : bool fc2 shape is False: (supercell, supecell, 3, 3) True: (primitive, supecell, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. fc_calculator : str or None Force constants calculator given by str. fc_calculator_options : dict Options for external force constants calculator. """ if displacement_dataset is None: if self._phonon_displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = self._phonon_displacement_dataset else: disp_dataset = displacement_dataset if forces_fc2 is not None: self.phonon_forces = forces_fc2 msg = ("Forces have to be stored in disp_dataset as written in " "this method's docstring for the type1 dataset.") warnings.warn(msg, DeprecationWarning) if is_compact_fc: p2s_map = self._phonon_primitive.p2s_map else: p2s_map = None if fc_calculator is not None: disps, forces = get_displacements_and_forces(disp_dataset) self._fc2 = get_fc2(self._phonon_supercell, self._phonon_primitive, disps, forces, fc_calculator=fc_calculator, fc_calculator_options=fc_calculator_options, atom_list=p2s_map, log_level=self._log_level) else: self._fc2 = get_phonopy_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset, atom_list=p2s_map) if symmetrize_fc2: if is_compact_fc: symmetrize_compact_force_constants(self._fc2, self._phonon_primitive) else: symmetrize_force_constants(self._fc2) def produce_fc3( self, forces_fc3=None, displacement_dataset=None, cutoff_distance=None, # set fc3 zero symmetrize_fc3r=False, is_compact_fc=False, fc_calculator=None, fc_calculator_options=None): """Calculate fc3 from displacements and forces Parameters ---------- forces_fc3 : Dummy argument displacement_dataset : dict Displacements in supercells. There are two types of formats. Type 1. Two atomic displacement in each supercell: {'natom': number of atoms in supercell, 'first_atoms': [ {'number': atom index of first displaced atom, 'displacement': displacement in Cartesian coordinates, 'forces': forces on atoms in supercell, 'second_atoms': [ {'number': atom index of second displaced atom, 'displacement': displacement in Cartesian coordinates}, 'forces': forces on atoms in supercell, ... ] }, ... ] } Type 2. All atomic displacements in each supercell: {'displacements': ndarray, dtype='double', order='C', shape=(supercells, atoms in supercell, 3) 'forces': ndarray, dtype='double',, order='C', shape=(supercells, atoms in supercell, 3)} In type 2, displacements and forces can be given by numpy array with different shape but that can be reshaped to (supercells, natom, 3). cutoff_distance : float After creating force constants, fc elements where any pair distance in atom triplets larger than cutoff_distance are set zero. symmetrize_fc3r : bool Only for type 1 displacement_dataset, translational and permutation symmetries are applied after creating fc3. This symmetrization is not very sophisticated and can break space group symmetry, but often useful. If better symmetrization is expected, it is recommended to use external force constants calculator such as ALM. Default is False. is_compact_fc : bool fc3 shape is False: (supercell, supercell, supecell, 3, 3, 3) True: (primitive, supercell, supecell, 3, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. fc_calculator : str or None Force constants calculator given by str. fc_calculator_options : dict Options for external force constants calculator. """ if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if forces_fc3 is not None: self.forces = forces_fc3 msg = ("Forces have to be stored in disp_dataset as written in " "this method's docstring for the type1 dataset.") warnings.warn(msg, DeprecationWarning) if fc_calculator is not None: from phono3py.other.fc_calculator import ( get_fc3, get_displacements_and_forces_fc3) disps, forces = get_displacements_and_forces_fc3(disp_dataset) fc2, fc3 = get_fc3(self._supercell, self._primitive, disps, forces, fc_calculator=fc_calculator, fc_calculator_options=fc_calculator_options, is_compact_fc=is_compact_fc, log_level=self._log_level) else: fc2, fc3 = get_phono3py_fc3(self._supercell, self._primitive, disp_dataset, self._symmetry, is_compact_fc=is_compact_fc, verbose=self._log_level) if symmetrize_fc3r: if is_compact_fc: set_translational_invariance_compact_fc3( fc3, self._primitive) set_permutation_symmetry_compact_fc3(fc3, self._primitive) if self._fc2 is None: symmetrize_compact_force_constants( fc2, self._primitive) else: set_translational_invariance_fc3(fc3) set_permutation_symmetry_fc3(fc3) if self._fc2 is None: symmetrize_force_constants(fc2) # Set fc2 and fc3 self._fc3 = fc3 # Normally self._fc2 is overwritten in produce_fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero( _fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self): if self._fc2 is not None: set_translational_invariance(self._fc2) if self._fc3 is not None: set_translational_invariance_fc3(self._fc3) @property def version(self): return __version__ def get_version(self): return self.version def get_interaction_strength(self): return self._interaction @property def fc2(self): return self._fc2 def get_fc2(self): return self.fc2 @fc2.setter def fc2(self, fc2): self._fc2 = fc2 def set_fc2(self, fc2): self.fc2 = fc2 @property def fc3(self): return self._fc3 def get_fc3(self): return self.fc3 @fc3.setter def fc3(self, fc3): self._fc3 = fc3 def set_fc3(self, fc3): self.fc3 = fc3 @property def nac_params(self): return self._nac_params def get_nac_params(self): return self.nac_params @property def primitive(self): return self._primitive def get_primitive(self): return self.primitive @property def unitcell(self): return self._unitcell def get_unitcell(self): return self.unitcell @property def supercell(self): return self._supercell def get_supercell(self): return self.supercell @property def phonon_supercell(self): return self._phonon_supercell def get_phonon_supercell(self): return self.phonon_supercell @property def phonon_primitive(self): return self._phonon_primitive def get_phonon_primitive(self): return self.phonon_primitive @property def symmetry(self): """return symmetry of supercell""" return self._symmetry def get_symmetry(self): return self.symmetry @property def primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self.primitive_symmetry @property def phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def get_phonon_supercell_symmetry(self): return self.phonon_supercell_symmetry @property def supercell_matrix(self): return self._supercell_matrix def get_supercell_matrix(self): return self.supercell_matrix @property def phonon_supercell_matrix(self): return self._phonon_supercell_matrix def get_phonon_supercell_matrix(self): return self.phonon_supercell_matrix @property def primitive_matrix(self): return self._primitive_matrix def get_primitive_matrix(self): return self.primitive_matrix @property def unit_conversion_factor(self): return self._frequency_factor_to_THz def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset @property def dataset(self): return self._displacement_dataset @property def displacement_dataset(self): return self.dataset def get_displacement_dataset(self): return self.displacement_dataset @property def phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_phonon_displacement_dataset(self): return self.phonon_displacement_dataset @property def supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_supercells_with_displacements(self): return self.supercells_with_displacements @property def phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def get_phonon_supercells_with_displacements(self): return self.phonon_supercells_with_displacements @property def mesh_numbers(self): return self._mesh_numbers def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False, output_filename=None): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, output_filename=output_filename, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh_numbers, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre solve_collective_phonon=False, use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, compression=None, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, solve_collective_phonon=solve_collective_phonon, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_gamma_detail=write_gamma_detail, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_frequency_shift(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), epsilons=None, output_filename=None): """Frequency shift from lowest order diagram is calculated. Args: epslins(list of float): The value to avoid divergence. When multiple values are given frequency shifts for those values are returned. """ if self._interaction is None: self.set_phph_interaction() if epsilons is None: _epsilons = [0.1] else: _epsilons = epsilons self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, _epsilons, temperatures, output_filename=output_filename, log_level=self._log_level) 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 _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) 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 """ self._primitive = self._get_primitive_cell(self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if (self._primitive is not None and (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any()): print(" Primitive cells for fc2 and fc3 can be different.") raise RuntimeError def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _guess_primitive_matrix(self): return guess_primitive_matrix(self._unitcell, symprec=self._symprec) 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._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[[ p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map() ]] self._phonon_supercell.set_masses(s_masses) def _set_mesh_numbers(self, mesh): _mesh = np.array(mesh) mesh_nums = None if _mesh.shape: if _mesh.shape == (3, ): mesh_nums = mesh elif self._primitive_symmetry is None: mesh_nums = length2mesh(mesh, self._primitive.get_cell()) else: rotations = self._primitive_symmetry.get_pointgroup_operations() mesh_nums = length2mesh(mesh, self._primitive.get_cell(), rotations=rotations) if mesh_nums is None: msg = "mesh has inappropriate type." raise TypeError(msg) self._mesh_numbers = mesh_nums
class Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3q = symmetrize_fc3q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix if type(primitive_matrix) is str and primitive_matrix == 'auto': self._primitive_matrix = self._guess_primitive_matrix() else: self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh_numbers = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._set_mesh_numbers(mesh) self.set_band_indices(band_indices) def set_band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack( self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None, solve_dynamical_matrices=True): if self._mesh_numbers is None: print("'mesh' has to be set in Phono3py instantiation.") raise RuntimeError self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh_numbers, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, frequency_scale_factor=frequency_scale_factor, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3q=self._symmetrize_fc3q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, solve_dynamical_matrices=solve_dynamical_matrices, verbose=self._log_level) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def get_phonon_data(self): if self._interaction is not None: grid_address = self._interaction.get_grid_address() freqs, eigvecs, _ = self._interaction.get_phonons() return freqs, eigvecs, grid_address else: msg = "set_phph_interaction has to be done." raise RuntimeError(msg) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: # 'is_diagonal=False' below is made intentionally. For # third-order force constants, we need better accuracy, # and I expect this choice is better for it, but not very # sure. # In phono3py, two atoms are displaced for each # configuration and the displacements are chosen, first # displacement from the perfect supercell, then second # displacement, considering symmetry. If I choose # is_diagonal=False for the first displacement, the # symmetry is less broken and the number of second # displacements can be smaller than in the case of # is_diagonal=True for the first displacement. This is # done in the call get_least_displacements() in # phonon3.displacement_fc3.get_third_order_displacements(). # # The call get_least_displacements() is only for the # second order force constants, but 'is_diagonal=False' to # be consistent with the above function call, and also for # the accuracy when calculating ph-ph interaction # strength because displacement directions are better to be # close to perpendicular each other to fit force constants. # # On the discussion of the accuracy, these are just my # expectation when I designed phono3py in the early time, # and in fact now I guess not very different. If these are # little different, then I should not surprise users to # change the default behaviour. At this moment, this is # open question and we will have more advance and should # have better specificy external software on this. phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = directions_to_displacement_dataset( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, symmetrize_fc2=False, is_compact_fc=False, use_alm=False, alm_options=None): if displacement_dataset is None: if self._phonon_displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = self._phonon_displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces if is_compact_fc: p2s_map = self._phonon_primitive.p2s_map else: p2s_map = None if use_alm: from phonopy.interface.alm import get_fc2 as get_fc2_alm self._fc2 = get_fc2_alm(self._phonon_supercell, self._phonon_primitive, disp_dataset, atom_list=p2s_map, alm_options=alm_options, log_level=self._log_level) else: self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset, atom_list=p2s_map) if symmetrize_fc2: if is_compact_fc: symmetrize_compact_force_constants( self._fc2, self._phonon_primitive) else: symmetrize_force_constants(self._fc2) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero symmetrize_fc3r=False, is_compact_fc=False, use_alm=False, alm_options=None): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc3 as get_fc3_alm fc2, fc3 = get_fc3_alm(self._supercell, self._primitive, forces_fc3, disp_dataset, self._symmetry, alm_options=alm_options, is_compact_fc=is_compact_fc, log_level=self._log_level) else: fc2, fc3 = self._get_fc3(forces_fc3, disp_dataset, is_compact_fc=is_compact_fc) if symmetrize_fc3r: if is_compact_fc: set_translational_invariance_compact_fc3( fc3, self._primitive) set_permutation_symmetry_compact_fc3(fc3, self._primitive) if self._fc2 is None: symmetrize_compact_force_constants(fc2, self._primitive) else: set_translational_invariance_fc3(fc3) set_permutation_symmetry_fc3(fc3) if self._fc2 is None: symmetrize_force_constants(fc2) # Set fc2 and fc3 self._fc3 = fc3 # Normally self._fc2 is overwritten in produce_fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero(_fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self): if self._fc2 is not None: set_translational_invariance(self._fc2) if self._fc3 is not None: set_translational_invariance_fc3(self._fc3) @property def version(self): return __version__ def get_version(self): return self.version def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 @property def nac_params(self): return self._nac_params def get_nac_params(self): return self.nac_params @property def primitive(self): return self._primitive def get_primitive(self): return self.primitive @property def unitcell(self): return self._unitcell def get_unitcell(self): return self.unitcell @property def supercell(self): return self._supercell def get_supercell(self): return self.supercell @property def phonon_supercell(self): return self._phonon_supercell def get_phonon_supercell(self): return self.phonon_supercell @property def phonon_primitive(self): return self._phonon_primitive def get_phonon_primitive(self): return self.phonon_primitive @property def symmetry(self): """return symmetry of supercell""" return self._symmetry def get_symmetry(self): return self.symmetry @property def primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self.primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry @property def supercell_matrix(self): return self._supercell_matrix def get_supercell_matrix(self): return self.supercell_matrix @property def phonon_supercell_matrix(self): return self._phonon_supercell_matrix def get_phonon_supercell_matrix(self): return self.phonon_supercell_matrix @property def primitive_matrix(self): return self._primitive_matrix def get_primitive_matrix(self): return self.primitive_matrix @property def unit_conversion_factor(self): return self._frequency_factor_to_THz def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset @property def displacement_dataset(self): return self._displacement_dataset def get_displacement_dataset(self): return self.displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements @property def mesh_numbers(self): return self._mesh_numbers def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False, output_filename=None): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, output_filename=output_filename, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh_numbers, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre solve_collective_phonon=False, use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, compression=None, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, solve_collective_phonon=solve_collective_phonon, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_gamma_detail=write_gamma_detail, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift( self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), epsilons=None, output_filename=None): """Frequency shift from lowest order diagram is calculated. Args: epslins(list of float): The value to avoid divergence. When multiple values are given frequency shifts for those values are returned. """ if self._interaction is None: self.set_phph_interaction() if epsilons is None: _epsilons = [0.1] else: _epsilons = epsilons self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, _epsilons, temperatures, output_filename=output_filename, log_level=self._log_level) 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 _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) 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 """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if (self._primitive is not None and (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any()): print(" Primitive cells for fc2 and fc3 can be different.") raise RuntimeError def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _guess_primitive_matrix(self): return guess_primitive_matrix(self._unitcell, symprec=self._symprec) 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._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[ [p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map()]] self._phonon_supercell.set_masses(s_masses) def _set_mesh_numbers(self, mesh): _mesh = np.array(mesh) mesh_nums = None if _mesh.shape: if _mesh.shape == (3,): mesh_nums = mesh elif self._primitive_symmetry is None: mesh_nums = length2mesh(mesh, self._primitive.get_cell()) else: rotations = self._primitive_symmetry.get_pointgroup_operations() mesh_nums = length2mesh(mesh, self._primitive.get_cell(), rotations=rotations) if mesh_nums is None: msg = "mesh has inappropriate type." raise TypeError(msg) self._mesh_numbers = mesh_nums def _get_fc3(self, forces_fc3, disp_dataset, is_compact_fc=False): count = 0 for disp1 in disp_dataset['first_atoms']: disp1['forces'] = forces_fc3[count] count += 1 for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 fc2, fc3 = get_fc3(self._supercell, self._primitive, disp_dataset, self._symmetry, is_compact_fc=is_compact_fc, verbose=self._log_level) return fc2, fc3
def run_spectral_function( interaction: Interaction, grid_points, temperatures=None, sigmas=None, frequency_points=None, frequency_step=None, num_frequency_points=None, num_points_in_batch=None, band_indices=None, write_txt=False, write_hdf5=False, output_filename=None, log_level=0, ): """Spectral function of self energy at frequency points. Band indices to be calculated at are kept in Interaction instance. Parameters ---------- interaction : Interaction Ph-ph interaction. grid_points : array_like Grid-point indices where imag-self-energeis are caclculated. dtype=int, shape=(grid_points,) temperatures : array_like Temperatures where imag-self-energies are calculated. dtype=float, shape=(temperatures,) sigmas : array_like, optional A set of sigmas. simgas=[None, ] means to use tetrahedron method, otherwise smearing method with real positive value of sigma. For example, sigmas=[None, 0.01, 0.03] is possible. Default is None, which results in [None, ]. dtype=float, shape=(sigmas,) frequency_points : array_like, optional Frequency sampling points. Default is None. With frequency_points_at_bands=False and frequency_points is None, num_frequency_points or frequency_step is used to generate uniform frequency sampling points. dtype=float, shape=(frequency_points,) frequency_step : float, optional Uniform pitch of frequency sampling points. Default is None. This results in using num_frequency_points. num_frequency_points : int, optional Number of sampling sampling points to be used instead of frequency_step. This number includes end points. Default is None, which gives 201. num_points_in_batch : int, optional Number of sampling points in one batch. This is for the frequency sampling mode and the sampling points are divided into batches. Lager number provides efficient use of multi-cores but more memory demanding. Default is None, which give the number of 10. band_indices : list Lists of list. Each list in list contains band indices. log_level: int Log level. Default is 0. Returns ------- SpectralFunction spf.spectral_functions.shape = (sigmas, temperatures, grid_points, band_indices, frequency_points) spf.half_linewidths, spf.shifts have the same shape as above. """ spf = SpectralFunction( interaction, grid_points, frequency_points=frequency_points, frequency_step=frequency_step, num_frequency_points=num_frequency_points, num_points_in_batch=num_points_in_batch, sigmas=sigmas, temperatures=temperatures, log_level=log_level, ) for i, gp in enumerate(spf): frequencies = interaction.get_phonons()[0] for sigma_i, sigma in enumerate(spf.sigmas): for t, spf_at_t in zip(temperatures, spf.spectral_functions[sigma_i, :, i]): for j, bi in enumerate(band_indices): pos = 0 for k in range(j): pos += len(band_indices[k]) if write_txt: filename = write_spectral_function_at_grid_point( gp, bi, spf.frequency_points, spf_at_t[pos:(pos + len(bi))].sum(axis=0) / len(bi), interaction.mesh_numbers, t, sigma=sigma, filename=output_filename, is_mesh_symmetry=interaction.is_mesh_symmetry, ) if log_level: print( f'Spectral functions were written to "{filename}".' ) if write_hdf5: filename = write_spectral_function_to_hdf5( gp, bi, temperatures, spf.spectral_functions[sigma_i, :, i], spf.shifts[sigma_i, :, i], spf.half_linewidths[sigma_i, :, i], interaction.mesh_numbers, interaction.bz_grid, sigma=sigma, frequency_points=spf.frequency_points, frequencies=frequencies[gp], all_band_exist=all_bands_exist(interaction), filename=output_filename, ) if log_level: print(f'Spectral functions were stored in "{filename}".') sys.stdout.flush() return spf
def get_real_self_energy( interaction: Interaction, grid_points, temperatures, epsilons=None, frequency_points=None, frequency_step=None, num_frequency_points=None, frequency_points_at_bands=False, write_hdf5=True, output_filename=None, log_level=0, ): """Real part of self energy at frequency points. Band indices to be calculated at are kept in Interaction instance. Parameters ---------- interaction : Interaction Ph-ph interaction. grid_points : array_like Grid-point indices where imag-self-energeis are caclculated. dtype=int, shape=(grid_points,) temperatures : array_like Temperatures where imag-self-energies are calculated. dtype=float, shape=(temperatures,) epsilons : array_like Smearing widths to computer principal part. When multiple values are given frequency shifts for those values are returned. dtype=float, shape=(epsilons,) frequency_points : array_like, optional Frequency sampling points. Default is None. With frequency_points_at_bands=False and frequency_points is None, num_frequency_points or frequency_step is used to generate uniform frequency sampling points. dtype=float, shape=(frequency_points,) frequency_step : float, optional Uniform pitch of frequency sampling points. Default is None. This results in using num_frequency_points. num_frequency_points: int, optional Number of sampling sampling points to be used instead of frequency_step. This number includes end points. Default is None, which gives 201. frequency_points_at_bands : bool, optional Phonon band frequencies are used as frequency points when True. Default is False. num_points_in_batch: int, optional Number of sampling points in one batch. This is for the frequency sampling mode and the sampling points are divided into batches. Lager number provides efficient use of multi-cores but more memory demanding. Default is None, which give the number of 10. log_level: int Log level. Default is 0. Returns ------- tuple : (frequency_points, all_deltas) are returned. When frequency_points_at_bands is True, all_deltas.shape = (epsilons, temperatures, grid_points, band_indices) otherwise all_deltas.shape = (epsilons, temperatures, grid_points, band_indices, frequency_points) """ if epsilons is None: _epsilons = [ None, ] else: _epsilons = epsilons _temperatures = np.array(temperatures, dtype="double") if (interaction.get_phonons()[2] == 0).any(): if log_level: print("Running harmonic phonon calculations...") interaction.run_phonon_solver() fst = RealSelfEnergy(interaction) mesh = interaction.mesh_numbers bz_grid = interaction.bz_grid # Set phonon at Gamma without NAC for finding max_phonon_freq. interaction.run_phonon_solver_at_gamma() max_phonon_freq = np.amax(interaction.get_phonons()[0]) interaction.run_phonon_solver_at_gamma(is_nac=True) band_indices = interaction.band_indices if frequency_points_at_bands: _frequency_points = None all_deltas = np.zeros( (len(_epsilons), len(_temperatures), len(grid_points), len(band_indices)), dtype="double", order="C", ) else: _frequency_points = get_frequency_points( max_phonon_freq=max_phonon_freq, sigmas=epsilons, frequency_points=frequency_points, frequency_step=frequency_step, num_frequency_points=num_frequency_points, ) all_deltas = np.zeros( ( len(_epsilons), len(_temperatures), len(grid_points), len(band_indices), len(_frequency_points), ), dtype="double", order="C", ) fst.frequency_points = _frequency_points for j, gp in enumerate(grid_points): fst.grid_point = gp if log_level: weights = interaction.get_triplets_at_q()[1] if len(grid_points) > 1: print( "------------------- Real part of self energy -o- (%d/%d) " "-------------------" % (j + 1, len(grid_points))) else: print("----------------------- Real part of self energy -o- " "-----------------------") print("Grid point: %d" % gp) print("Number of ir-triplets: %d / %d" % (len(weights), weights.sum())) fst.run_interaction() frequencies = interaction.get_phonons()[0][gp] if log_level: bz_grid = interaction.bz_grid qpoint = np.dot(bz_grid.QDinv, bz_grid.addresses[gp]) print("Phonon frequencies at (%4.2f, %4.2f, %4.2f):" % tuple(qpoint)) for bi, freq in enumerate(frequencies): print("%3d %f" % (bi + 1, freq)) sys.stdout.flush() for i, epsilon in enumerate(_epsilons): fst.epsilon = epsilon for k, t in enumerate(_temperatures): fst.temperature = t fst.run() all_deltas[i, k, j] = fst.real_self_energy.T # if not frequency_points_at_bands: # pos = 0 # for bi_set in [[bi, ] for bi in band_indices]: # filename = write_real_self_energy( # gp, # bi_set, # _frequency_points, # all_deltas[i, k, j, pos:(pos + len(bi_set))], # mesh, # fst.epsilon, # t, # filename=output_filename) # pos += len(bi_set) # if log_level: # print("Real part of self energies were stored in " # "\"%s\"." % filename) # sys.stdout.flush() if write_hdf5: filename = write_real_self_energy_to_hdf5( gp, band_indices, _temperatures, all_deltas[i, :, j], mesh, fst.epsilon, bz_grid=bz_grid, frequency_points=_frequency_points, frequencies=frequencies, filename=output_filename, ) if log_level: print('Real part of self energies were stored in "%s".' % filename) sys.stdout.flush() return _frequency_points, all_deltas