def get_bandstructure(self): phonopy_obj = self.phonopy_obj frequency_unit_factor = VaspToTHz is_eigenvectors = False unit_cell = phonopy_obj.unitcell.get_cell() sym_tol = phonopy_obj.symmetry.tolerance if self.band_conf is not None: parameters = read_kpath(self.band_conf) else: parameters = generate_kpath_ase(unit_cell, sym_tol) if not parameters: return None, None, None # Distances calculated in phonopy.band_structure.BandStructure object # are based on absolute positions of q-points in reciprocal space # as calculated by using the cell which is handed over during instantiation. # Fooling that object by handing over a "unit cell" diag(1,1,1) instead clashes # with calculation of non-analytical terms. # Hence generate appropriate distances and special k-points list based on fractional # coordinates in reciprocal space (to keep backwards compatibility with previous # FHI-aims phonon implementation). bands = [] bands_distances = [] distance = 0.0 bands_special_points = [distance] bands_labels = [] label = parameters[0]["startname"] for b in parameters: kstart = np.array(b["kstart"]) kend = np.array(b["kend"]) npoints = b["npoints"] dk = (kend - kstart) / (npoints - 1) bands.append([(kstart + dk * n) for n in range(npoints)]) dk_length = np.linalg.norm(dk) for n in range(npoints): bands_distances.append(distance + dk_length * n) distance += dk_length * (npoints - 1) bands_special_points.append(distance) label = [b["startname"], b["endname"]] bands_labels.append(label) bs_obj = BandStructure(bands, phonopy_obj.dynamical_matrix, with_eigenvectors=is_eigenvectors, factor=frequency_unit_factor) freqs = bs_obj.get_frequencies() return np.array(freqs), np.array(bands), np.array(bands_labels)
def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): self._set_dynamical_matrix() self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor)
def get_band_structure(self, band_paths: list, labels: list = None, npoints: int = 51, with_eigenvectors: bool = False, use_reciprocal_lattice: bool = True): """ Get BandStructure class object. Args: band_paths (list): Band paths. labels (list): Band labels. npoints (int): The number of sampling points. with_eigenvectors (bool): If True, calculte eigenvectors. """ if use_reciprocal_lattice: rec_lattice = self._reciprocal_lattice else: rec_lattice = None qpoints, connections = get_band_qpoints_and_path_connections( band_paths=band_paths, npoints=npoints, rec_lattice=rec_lattice) band_structure = BandStructure( paths=qpoints, dynamical_matrix=self._phonon.get_dynamical_matrix(), with_eigenvectors=with_eigenvectors, is_band_connection=False, group_velocity=None, path_connections=connections, labels=labels, is_legacy_plot=False) return band_structure
def get_axes_distances(band_structure: BandStructure) -> list: """ Get axes distances in angstrome. Args: band_structure: BandStructure class object. Returns: list: Axes distances in angstrome. Note: This function returns list of distances of connected band paths. To plot band structure, axes ratios are necessary. You can use resultant list as ratios for plotting band structure. """ min_distance = 0. widths = [] for distance, path_connection in zip(band_structure.get_distances(), band_structure.path_connections): if path_connection: continue width = distance[-1] - min_distance widths.append(width) min_distance = distance[-1] return widths
def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._band_structure = None return False self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) return True
def set_band_structure( self, bands, is_eigenvectors=False ): self.__band_structure = BandStructure( bands, self.dynamical_matrix, self.primitive, is_eigenvectors=is_eigenvectors, factor=self.factor )
class Phonopy(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=None, factor=VaspToTHz, is_auto_displacements=None, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): if is_auto_displacements is not None: print("Warning: \'is_auto_displacements\' argument is obsolete.") if is_auto_displacements is False: print("Sets of displacements are not created as default.") else: print("Use \'generate_displacements\' method explicitly to " "create sets of displacements.") if distance is not None: print("Warning: \'distance\' keyword argument is obsolete at " "Phonopy instantiation.") print("Specify \'distance\' keyword argument when calling " "\'generate_displacements\'") print("method (See the Phonopy API document).") self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = Atoms(atoms=unitcell) self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def get_version(self): return __version__ def get_primitive(self): return self._primitive primitive = property(get_primitive) def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def get_symmetry(self): """return symmetry of supercell""" return self._symmetry symmetry = property(get_symmetry) def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_supercell_matrix(self): return self._supercell_matrix def get_primitive_matrix(self): return self._primitive_matrix def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def get_displacement_dataset(self): return self._displacement_dataset def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_supercells_with_displacements(self): if self._displacement_dataset is None: return None else: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def get_nac_params(self): return self._nac_params def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_unitcell(self, unitcell): self._unitcell = unitcell self._build_supercell() self._build_primitive_cell() self._search_symmetry() self._search_primitive_symmetry() self._displacement_dataset = None def set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._set_dynamical_matrix() def set_nac_params(self, nac_params=None): self._nac_params = nac_params self._set_dynamical_matrix() def set_displacement_dataset(self, displacement_dataset): """ displacement_dataset: {'natom': number_of_atoms_in_supercell, 'first_atoms': [ {'number': atom index of displaced atom, 'displacement': displacement in Cartesian coordinates, 'direction': displacement direction with respect to axes 'forces': forces on atoms in supercell}, {...}, ...]} """ self._displacement_dataset = displacement_dataset self._displacements = [] self._displacement_directions = [] for disp in self._displacement_dataset['first_atoms']: x = disp['displacement'] self._displacements.append([disp['number'], x[0], x[1], x[2]]) if 'direction' in disp: y = disp['direction'] self._displacement_directions.append( [disp['number'], y[0], y[1], y[2]]) if not self._displacement_directions: self._displacement_directions = None def set_forces(self, sets_of_forces): """ sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] """ for disp, forces in zip( self._displacement_dataset['first_atoms'], sets_of_forces): disp['forces'] = forces def set_force_constants(self, force_constants): self._force_constants = force_constants self._set_dynamical_matrix() def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) self._set_dynamical_matrix() def set_dynamical_matrix(self): self._set_dynamical_matrix() def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset) def produce_force_constants(self, forces=None, calculate_full_force_constants=True, computation_algorithm="svd"): if forces is not None: self.set_forces(forces) # A primitive check if 'forces' key is in displacement_dataset. for disp in self._displacement_dataset['first_atoms']: if 'forces' not in disp: return False if calculate_full_force_constants: self._run_force_constants_from_forces( decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self._run_force_constants_from_forces( distributed_atom_list=p2s_map, decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) self._set_dynamical_matrix() return True def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) self._set_dynamical_matrix() def symmetrize_force_constants_by_space_group(self): from phonopy.harmonic.force_constants import (set_tensor_symmetry, set_tensor_symmetry_PJ) set_tensor_symmetry_PJ(self._force_constants, self._supercell.get_cell().T, self._supercell.get_scaled_positions(), self._symmetry) self._set_dynamical_matrix() ##################### # Phonon properties # ##################### # Single q-point def get_dynamical_matrix_at_q(self, q): self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors # Band structure def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._band_structure = None return False self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) return True def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, labels=None): import matplotlib.pyplot as plt if labels: from matplotlib import rc rc('text', usetex=True) fig, ax = plt.subplots() ax.xaxis.set_ticks_position('both') ax.yaxis.set_ticks_position('both') ax.xaxis.set_tick_params(which='both', direction='in') ax.yaxis.set_tick_params(which='both', direction='in') self._band_structure.plot(plt, labels=labels) return plt def write_yaml_band_structure(self, labels=None, comment=None, filename="band.yaml"): self._band_structure.write_yaml(labels=labels, comment=comment, filename=filename) # Sampling mesh def run_mesh(self): if self._mesh is not None: self._mesh.run() def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_mesh_symmetry=True, is_eigenvectors=False, is_gamma_center=False, run_immediately=True): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._mesh = None return False self._mesh = Mesh( self._dynamical_matrix, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_mesh_symmetry, is_eigenvectors=is_eigenvectors, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor, use_lapack_solver=self._use_lapack_solver) if run_immediately: self._mesh.run() return True def get_mesh(self): if self._mesh is None: return None else: return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def get_mesh_grid_info(self): if self._mesh is None: return None else: return (self._mesh.get_grid_address(), self._mesh.get_ir_grid_points(), self._mesh.get_grid_mapping_table()) def write_hdf5_mesh(self): self._mesh.write_hdf5() def write_yaml_mesh(self): self._mesh.write_yaml() # Plot band structure and DOS (PDOS) together def plot_band_structure_and_dos(self, pdos_indices=None, labels=None): import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec if labels: from matplotlib import rc rc('text', usetex=True) plt.figure(figsize=(10, 6)) gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1]) ax1 = plt.subplot(gs[0, 0]) ax1.xaxis.set_ticks_position('both') ax1.yaxis.set_ticks_position('both') ax1.xaxis.set_tick_params(which='both', direction='in') ax1.yaxis.set_tick_params(which='both', direction='in') self._band_structure.plot(plt, labels=labels) ax2 = plt.subplot(gs[0, 1], sharey=ax1) ax2.xaxis.set_ticks_position('both') ax2.yaxis.set_ticks_position('both') ax2.xaxis.set_tick_params(which='both', direction='in') ax2.yaxis.set_tick_params(which='both', direction='in') plt.subplots_adjust(wspace=0.03) plt.setp(ax2.get_yticklabels(), visible=False) if pdos_indices is None: self._total_dos.plot(plt, ylabel="", draw_grid=False, flip_xy=True) else: self._pdos.plot(plt, indices=pdos_indices, ylabel="", draw_grid=False, flip_xy=True) ax2.set_xlim((0, None)) return plt # DOS def set_total_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False): if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before DOS calculation.") self._total_dos = None return False total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(freq_min, freq_max, freq_pitch) total_dos.run() self._total_dos = total_dos return True def get_total_DOS(self): """ Retern frequencies and total dos. The first element is freqs and the second is total dos. frequencies: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.xaxis.set_ticks_position('both') ax.yaxis.set_ticks_position('both') ax.xaxis.set_tick_params(which='both', direction='in') ax.yaxis.set_tick_params(which='both', direction='in') self._total_dos.plot(plt, draw_grid=False) ax.set_ylim((0, None)) return plt def write_total_DOS(self): self._total_dos.write() # PDOS def set_partial_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False, direction=None, xyz_projection=False): self._pdos = None if self._mesh is None: print("Warning: \'set_mesh\' has to be called before " "PDOS calculation.") return False if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False num_grid = np.prod(self._mesh.get_mesh_numbers()) if num_grid != len(self._mesh.get_ir_grid_points()): print("Warning: \'set_mesh\' has to be called with " "is_mesh_symmetry=False.") return False if direction is not None: direction_cart = np.dot(direction, self._primitive.get_cell()) else: direction_cart = None self._pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method, direction=direction_cart, xyz_projection=xyz_projection) self._pdos.set_draw_area(freq_min, freq_max, freq_pitch) self._pdos.run() return True def get_partial_DOS(self): """ Retern frequencies and partial_dos. The first element is freqs and the second is partial_dos. frequencies: [freq1, freq2, ...] partial_dos: [[atom1-freq1, atom1-freq2, ...], [atom2-freq1, atom2-freq2, ...], ...] """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.xaxis.set_ticks_position('both') ax.yaxis.set_ticks_position('both') ax.xaxis.set_tick_params(which='both', direction='in') ax.yaxis.set_tick_params(which='both', direction='in') self._pdos.plot(plt, indices=pdos_indices, legend=legend, draw_grid=False) ax.set_ylim((0, None)) return plt def write_partial_DOS(self): self._pdos.write() # Thermal property def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, temperatures=None, is_projection=False, band_indices=None, cutoff_frequency=None, pretend_real=False): if self._mesh is None: print("Warning: set_mesh has to be done before " "set_thermal_properties") return False else: tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, band_indices=band_indices, cutoff_frequency=cutoff_frequency, pretend_real=pretend_real) if temperatures is None: tp.set_temperature_range(t_step=t_step, t_max=t_max, t_min=t_min) else: tp.set_temperatures(temperatures) tp.run() self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.xaxis.set_ticks_position('both') ax.yaxis.set_ticks_position('both') ax.xaxis.set_tick_params(which='both', direction='in') ax.yaxis.set_tick_params(which='both', direction='in') self._thermal_properties.plot(plt) temps, _, _, _ = self._thermal_properties.get_thermal_properties() ax.set_xlim((0, temps[-1])) return plt def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) # Thermal displacement def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, temperatures=None, direction=None, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacements = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacements\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) if temperatures is None: td.set_temperature_range(t_min, t_max, t_step) else: td.set_temperatures(temperatures) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) td.run() self._thermal_displacements = td return True def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): import matplotlib.pyplot as plt fig, ax = plt.subplots() ax.xaxis.set_ticks_position('both') ax.yaxis.set_ticks_position('both') ax.xaxis.set_tick_params(which='both', direction='in') ax.yaxis.set_tick_params(which='both', direction='in') self._thermal_displacements.plot(plt, is_legend=is_legend) temps, _ = self._thermal_displacements.get_thermal_displacements() ax.set_xlim((0, temps[-1])) return plt def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() # Thermal displacement matrix def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None, t_cif=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacement_matrices = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacement_matrices\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency, lattice=self._primitive.get_cell().T) if t_cif is None: tdm.set_temperature_range(t_min, t_max, t_step) else: tdm.set_temperatures([t_cif]) tdm.run() self._thermal_displacement_matrices = tdm return True def get_thermal_displacement_matrices(self): tdm = self._thermal_displacement_matrices if tdm is not None: return tdm.get_thermal_displacement_matrices() def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def write_thermal_displacement_matrix_to_cif(self, temperature_index): self._thermal_displacement_matrices.write_cif(self._primitive, temperature_index) # Mean square distance between a pair of atoms def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) td.run(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() # Sampling at q-points def set_qpoints_phonon(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._qpoints_phonon = None return False self._qpoints_phonon = QpointsPhonon( np.reshape(q_points, (-1, 3)), self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) return True def get_qpoints_phonon(self): return (self._qpoints_phonon.get_frequencies(), self._qpoints_phonon.get_eigenvectors()) def write_hdf5_qpoints_phonon(self): self._qpoints_phonon.write_hdf5() def write_yaml_qpoints_phonon(self): self._qpoints_phonon.write_yaml() # Normal mode animation def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return False if q_point is None: animation = Animation([0, 0, 0], self._dynamical_matrix, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index is None or amplitude is None or num_div is None: print("Warning: Parameters are not correctly set for " "animation.") return False if anime_type == 'arc' or anime_type is None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) return True # Atomic modulation of normal mode def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._modulation = None return False self._modulation = Modulation(self._dynamical_matrix, dimension, phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() return True def get_modulated_supercells(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulated_supercells() def get_modulations_and_supercell(self): """Return modulations and supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_modulations_and_supercell() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() # Irreducible representation def set_irreps(self, q, is_little_cogroup=False, nac_q_direction=None, degeneracy_tolerance=1e-4): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._irreps = None return None self._irreps = IrReps( self._dynamical_matrix, q, is_little_cogroup=is_little_cogroup, nac_q_direction=nac_q_direction, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) # Group velocity def set_group_velocity(self, q_length=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._group_velocity = None return False self._group_velocity = GroupVelocity( self._dynamical_matrix, q_length=q_length, symmetry=self._primitive_symmetry, frequency_factor_to_THz=self._factor) return True def get_group_velocity(self): return self._group_velocity.get_group_velocity() def get_group_velocity_at_q(self, q_point): if self._group_velocity is None: self.set_group_velocity() self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] # Moment def set_moment(self, order=1, is_projection=False, freq_min=None, freq_max=None): if self._mesh is None: print("Warning: set_mesh has to be done before set_moment") return False else: if is_projection: if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors()) else: moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights()) if freq_min is not None or freq_max is not None: moment.set_frequency_range(freq_min=freq_min, freq_max=freq_max) moment.run(order=order) self._moment = moment.get_moment() return True def get_moment(self): return self._moment ################# # Local methods # ################# def _run_force_constants_from_forces(self, distributed_atom_list=None, decimals=None, computation_algorithm="svd"): if self._displacement_dataset is not None: self._force_constants = get_fc2( self._supercell, self._symmetry, self._displacement_dataset, atom_list=distributed_atom_list, decimals=decimals, computation_algorithm=computation_algorithm) def _set_dynamical_matrix(self): self._dynamical_matrix = None if (self._supercell is None or self._primitive is None): print("Bug: Supercell or primitive is not created.") return False elif self._force_constants is None: print("Warning: Force constants are not prepared.") return False elif self._primitive.get_masses() is None: print("Warning: Atomic masses are not correctly set.") return False else: if self._nac_params is None: self._dynamical_matrix = DynamicalMatrix( self._supercell, self._primitive, self._force_constants, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) else: self._dynamical_matrix = DynamicalMatrixNAC( self._supercell, self._primitive, self._force_constants, nac_params=self._nac_params, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) return True def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: Point group symmetries of supercell and primitive" "cell are different.") def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_supercells_with_displacements(self): supercells = [] for disp in self._displacement_dataset['first_atoms']: positions = self._supercell.get_positions() positions[disp['number']] += disp['displacement'] supercells.append(Atoms( numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) if self._primitive_matrix is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix) self._primitive = get_primitive( self._supercell, trans_mat, self._symprec) num_satom = self._supercell.get_number_of_atoms() num_patom = self._primitive.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False
class Phonopy: def __init__(self, unitcell, supercell_matrix, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, symprec=1e-5, is_symmetry=True, log_level=0): self._symprec = symprec self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._factor = factor self._is_symmetry = is_symmetry self._log_level = log_level self._supercell = None self._set_supercell() self._symmetry = None self._set_symmetry() # set_displacements (used only in preprocess) self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None if is_auto_displacements: self.generate_displacements(distance) # set_post_process self._primitive = None self._dynamical_matrix = None self._is_nac = False # set_force_constants or set_forces self._set_of_forces_objects = None self._force_constants = None # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def get_primitive(self): return self._primitive primitive = property(get_primitive) def set_primitive(self, primitive): self._primitive = primitive def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def set_supercell(self, supercell): self._supercell = supercell def get_symmetry(self): return self._symmetry symmetry = property(get_symmetry) def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacements: List of displacements in Cartesian coordinates. See 'set_displacements' displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ lattice = self._supercell.get_cell() self._displacements = [] self._displacement_directions = \ get_least_displacements(self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) for disp in self._displacement_directions: atom_num = disp[0] disp_cartesian = np.dot(disp[1:], lattice) disp_cartesian *= distance / np.linalg.norm(disp_cartesian) self._displacements.append([ atom_num, disp_cartesian[0], disp_cartesian[1], disp_cartesian[2] ]) self._set_supercells_with_displacements() def set_displacements(self, displacements): """Set displacements manually displacemsts: List of disctionaries [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates """ self._displacements = displacements self._set_supercells_with_displacements() def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_supercells_with_displacements(self): return self._supercells_with_displacements def set_post_process(self, primitive_matrix=np.eye(3, dtype=float), sets_of_forces=None, set_of_forces_objects=None, force_constants=None, is_nac=False, calculate_full_force_constants=False, force_constants_decimals=None, dynamical_matrix_decimals=None): """ Set forces or force constants to prepare phonon calculations. The order of 'sets_of_forces' has to correspond to that of 'displacements' that should be already stored. 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 sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] set_of_forces_objects: [FORCES_object, FORCES_object, FORCES_object, ...] """ self._is_nac = is_nac # Primitive cell inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) self._primitive = Primitive( self._supercell, np.dot(inv_supercell_matrix, primitive_matrix), self._symprec) # Set set of FORCES objects or force constants if sets_of_forces is not None: self.set_forces(sets_of_forces) elif set_of_forces_objects is not None: self.set_force_sets(set_of_forces_objects) elif force_constants is not None: self.set_force_constants(force_constants) # Calculate force cosntants from forces (full or symmetry reduced) if self._set_of_forces_objects is not None: if calculate_full_force_constants: self.set_force_constants_from_forces( distributed_atom_list=None, force_constants_decimals=force_constants_decimals) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self.set_force_constants_from_forces( distributed_atom_list=p2s_map, force_constants_decimals=force_constants_decimals) if self._force_constants is None: print "In set_post_process, sets_of_forces or force_constants" print "has to be set." return False # Dynamical Matrix self.set_dynamical_matrix(decimals=dynamical_matrix_decimals) def set_nac_params(self, nac_params, method='wang'): if self._is_nac: self._dynamical_matrix.set_nac_params(nac_params, method) def set_dynamical_matrix(self, decimals=None): if self._is_nac: self._dynamical_matrix = \ DynamicalMatrixNAC(self._supercell, self._primitive, self._force_constants, decimals=decimals, symprec=self._symprec) else: self._dynamical_matrix = \ DynamicalMatrix(self._supercell, self._primitive, self._force_constants, decimals=decimals, symprec=self._symprec) def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_forces(self, sets_of_forces): forces = [] for i, disp in enumerate(self._displacements): forces.append(Forces(disp[0], disp[1:4], sets_of_forces[i])) self._set_of_forces_objects = forces def set_force_constants_from_forces(self, distributed_atom_list=None, force_constants_decimals=None): self._force_constants = get_force_constants( self._set_of_forces_objects, self._symmetry, self._supercell, atom_list=distributed_atom_list, decimals=force_constants_decimals) def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) def set_force_constants(self, force_constants): self._force_constants = force_constants def set_force_sets(self, sets_of_forces_objects): self._set_of_forces_objects = sets_of_forces_objects def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def get_dynamical_matrix_at_q(self, q): self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() # Frequency at a q-point def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor # Frequency and eigenvector at a q-point def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors ## This expression may not be supported in old python versions. # frequencies = np.array( # [np.sqrt(x) if x > 0 else -np.sqrt(-x) for x in eigvals]) # return frequencies * self._factor, eigenvectors # Band structure def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): self._band_structure = BandStructure( bands, self._dynamical_matrix, self._primitive, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, symbols=None): return self._band_structure.plot_band(symbols) def write_yaml_band_structure(self): self._band_structure.write_yaml() # Mesh sampling def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_symmetry=True, is_band_connection=False, is_eigenvectors=False, is_gamma_center=False): self._mesh = Mesh(self._dynamical_matrix, self._primitive, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_symmetry, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, factor=self._factor, symprec=self._symprec) def get_mesh(self): return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def write_yaml_mesh(self): self._mesh.write_yaml() def write_hdf5_mesh(self): self._mesh.write_hdf5() # Thermal property def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, is_projection=False, cutoff_frequency=None): if self._mesh == None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, cutoff_frequency=cutoff_frequency) tp.set_thermal_properties(t_step, t_max, t_min) self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): return self._thermal_properties.plot_thermal_properties() def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) # Partial DOS def set_partial_DOS(self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None, tetrahedron_method=False): if self._mesh == None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self._mesh.get_eigenvectors() == None: print "Eigenvectors have to be calculated." sys.exit(1) pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) pdos.set_draw_area(omega_min, omega_max, omega_pitch) pdos.calculate() self._pdos = pdos def get_partial_DOS(self): """ Retern omegas and partial_dos. The first element is omegas and the second is partial_dos. omegas: [freq1, freq2, ...] partial_dos: [[elem1-freq1, elem1-freq2, ...], [elem2-freq1, elem2-freq2, ...], ...] where elem1: atom1-x compornent elem2: atom1-y compornent elem3: atom1-z compornent elem4: atom2-x compornent ... """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): return self._pdos.plot_pdos(indices=pdos_indices, legend=legend) def write_partial_DOS(self): self._pdos.write() # Total DOS def set_total_DOS(self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None, tetrahedron_method=False): if self._mesh == None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(omega_min, omega_max, omega_pitch) total_dos.calculate() self._total_dos = total_dos def get_total_DOS(self): """ Retern omegas and total dos. The first element is omegas and the second is total dos. omegas: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): return self._total_dos.plot_dos() def write_total_DOS(self): self._total_dos.write() # Thermal displacement def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, direction=None, cutoff_eigenvalue=None): """ cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) direction: Projection direction in reduced coordinates """ if self._mesh == None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_eigenvalue=cutoff_eigenvalue) td.set_temperature_range(t_min, t_max, t_step) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) # td.run() td.run_mesh() self._thermal_displacements = td def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): return self._thermal_displacements.plot(is_legend) def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() def write_hdf5_thermal_displacements(self): self._thermal_displacements.write_hdf5() # Thermal displacement matrices def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None): """ cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) direction: Projection direction in reduced coordinates """ if self._mesh == None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_eigenvalue=cutoff_eigenvalue) tdm.set_temperature_range(t_min, t_max, t_step) # tdm.run() tdm.run_mesh() self._thermal_displacement_matrices = tdm def get_thermal_displacement_matrices(self): if self._thermal_displacement_matrices is not None: return self._thermal_displacement_matrices.get_thermal_displacement_matrices( ) def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def write_hdf5_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_hdf5() # Thermal displacement def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), symprec=self._symprec, cutoff_eigenvalue=cutoff_eigenvalue) td.set_temperature_range(t_min, t_max, t_step) # td.run(atom_pairs) td.run_mesh(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() def write_hdf5_thermal_distances(self): self._thermal_distances.write_hdf5() # Q-points mode def write_yaml_qpoints(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False, factor=VaspToTHz): write_yaml_qpoints(q_points, self._primitive, self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) # Animation def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): if q_point == None: animation = Animation([0, 0, 0], self._dynamical_matrix, self._primitive, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, self._primitive, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index == None or amplitude == None or num_div == None: print "Parameters are not correctly set for animation." sys.exit(1) if anime_type == 'arc' or anime_type == None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) # Modulation def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): self._modulation = Modulation(self._dynamical_matrix, self._primitive, dimension=dimension, phonon_modes=phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() def get_modulations(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulations() def get_delta_modulations(self): """Return modulations relative to equilibrium supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_delta_modulations() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() # Characters of irreducible representations def set_irreps(self, q, degeneracy_tolerance=1e-4): self._irreps = IrReps(self._dynamical_matrix, q, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) # Group velocity def set_group_velocity(self, q_points=None, q_length=1e-4): self._group_velocity = GroupVelocity( self._dynamical_matrix, q_points=q_points, symmetry=self._symmetry, q_length=q_length, frequency_factor_to_THz=self._factor) def get_group_velocity(self, q_point): self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] def _set_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _set_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _set_supercells_with_displacements(self): supercells = [] for disp in self._displacements: positions = self._supercell.get_positions() positions[disp[0]] += disp[1:4] supercells.append( Atoms(numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells
class Phonopy: def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, log_level=0): self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None if is_auto_displacements: self.generate_displacements(distance=distance) # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def set_post_process(self, primitive_matrix=None, sets_of_forces=None, displacement_dataset=None, force_constants=None, is_nac=None): print print ("********************************** Warning" "**********************************") print "set_post_process will be obsolete." print (" produce_force_constants is used instead of set_post_process" " for producing") print (" force constants from forces.") if primitive_matrix is not None: print (" primitive_matrix has to be given at Phonopy::__init__" " object creation.") print ("******************************************" "**********************************") print if primitive_matrix is not None: self._primitive_matrix = primitive_matrix self._build_primitive_cell() self._search_primitive_symmetry() if sets_of_forces is not None: self.set_forces(sets_of_forces) elif displacement_dataset is not None: self._displacement_dataset = displacement_dataset elif force_constants is not None: self.set_force_constants(force_constants) if self._displacement_dataset is not None: self.produce_force_constants() 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) def get_primitive(self): return self._primitive primitive = property(get_primitive) def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def set_supercell(self, supercell): self._supercell = supercell def get_symmetry(self): """return symmetry of supercell""" return self._symmetry symmetry = property(get_symmetry) def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def produce_force_constants(self, forces=None, calculate_full_force_constants=True, computation_algorithm="svd"): if forces is not None: self.set_forces(forces) if calculate_full_force_constants: self._run_force_constants_from_forces( decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self._run_force_constants_from_forces( distributed_atom_list=p2s_map, decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) def set_nac_params(self, nac_params=None, method=None): if method is not None: print "set_nac_params:" print " Keyword argument of \"method\" is not more supported." self._nac_params = nac_params def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset) def set_displacements(self, displacements): print print ("********************************** Warning" "**********************************") print "set_displacements is obsolete. Do nothing." print ("******************************************" "**********************************") print def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_displacement_dataset(self): return self._displacement_dataset def get_supercells_with_displacements(self): if self._displacement_dataset is None: return None else: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_forces(self, sets_of_forces): """ sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] """ for disp, forces in zip( self._displacement_dataset['first_atoms'], sets_of_forces): disp['forces'] = forces def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) def set_force_constants(self, force_constants): self._force_constants = force_constants def set_force_sets(self, force_sets): print print ("********************************** Warning" "**********************************") print "set_force_sets will be obsolete." print (" The method name is changed to set_displacement_dataset.") print ("******************************************" "**********************************") print self.set_displacement_dataset(force_sets) def set_displacement_dataset(self, displacement_dataset): """ displacement_dataset: {'natom': number_of_atoms_in_supercell, 'first_atoms': [ {'number': atom index of displaced atom, 'displacement': displacement in Cartesian coordinates, 'direction': displacement direction with respect to axes 'forces': forces on atoms in supercell}, {...}, ...]} """ self._displacement_dataset = displacement_dataset self._displacements = [] self._displacement_directions = [] for disp in self._displacement_dataset['first_atoms']: x = disp['displacement'] self._displacements.append([disp['number'], x[0], x[1], x[2]]) if 'direction' in disp: y = disp['direction'] self._displacement_directions.append( [disp['number'], y[0], y[1], y[2]]) if not self._displacement_directions: self._displacement_directions = None def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) def symmetrize_force_constants_by_space_group(self): rotations = self._symmetry.get_symmetry_operations()['rotations'] translations = self._symmetry.get_symmetry_operations()['translations'] set_tensor_symmetry(self._force_constants, self._supercell.get_cell().T, self._supercell.get_scaled_positions(), rotations, translations, self._symprec) def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def set_dynamical_matrix(self): self._set_dynamical_matrix() def get_dynamical_matrix_at_q(self, q): self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): self._set_dynamical_matrix() self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, symbols=None): return self._band_structure.plot_band(symbols) def write_yaml_band_structure(self): self._band_structure.write_yaml() def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_mesh_symmetry=True, is_eigenvectors=False, is_gamma_center=False): self._set_dynamical_matrix() self._mesh = Mesh( self._dynamical_matrix, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_mesh_symmetry, is_eigenvectors=is_eigenvectors, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor) def get_mesh(self): return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def write_yaml_mesh(self): self._mesh.write_yaml() def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, is_projection=False, band_indices=None, cutoff_frequency=None): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" return False else: tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, band_indices=band_indices, cutoff_frequency=cutoff_frequency) tp.set_thermal_properties(t_step=t_step, t_max=t_max, t_min=t_min) self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): return self._thermal_properties.plot_thermal_properties() def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) def set_partial_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False, direction=None): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if direction is not None: direction_cart = np.dot(direction, self._primitive.get_cell()) else: direction_cart = None pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method, direction=direction_cart) pdos.set_draw_area(freq_min, freq_max, freq_pitch) pdos.run() self._pdos = pdos def get_partial_DOS(self): """ Retern frequencies and partial_dos. The first element is freqs and the second is partial_dos. frequencies: [freq1, freq2, ...] partial_dos: [[atom1-freq1, atom1-freq2, ...], [atom2-freq1, atom2-freq2, ...], ...] """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): return self._pdos.plot_pdos(indices=pdos_indices, legend=legend) def write_partial_DOS(self): self._pdos.write() def set_total_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(freq_min, freq_max, freq_pitch) total_dos.run() self._total_dos = total_dos def get_total_DOS(self): """ Retern frequencies and total dos. The first element is freqs and the second is total dos. frequencies: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): return self._total_dos.plot_dos() def write_total_DOS(self): self._total_dos.write() def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, direction=None, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) td.run() self._thermal_displacements = td def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): return self._thermal_displacements.plot(is_legend) def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) tdm.set_temperature_range(t_min, t_max, t_step) tdm.run() self._thermal_displacement_matrices = tdm def get_thermal_displacement_matrices(self): if self._thermal_displacement_matrices is not None: return self._thermal_displacement_matrices.get_thermal_displacement_matrices() def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) td.run(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() def set_qpoints_phonon(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False, factor=VaspToTHz): self._set_dynamical_matrix() self._qpoints_phonon = QpointsPhonon( q_points, self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) def get_qpoints_phonon(self): return (self._qpoints_phonon.get_frequencies(), self._qpoints_phonon.get_eigenvectors()) def write_yaml_qpoints_phonon(self): self._qpoints_phonon.write_yaml() def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): self._set_dynamical_matrix() if q_point is None: animation = Animation([0, 0, 0], self._dynamical_matrix, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index is None or amplitude is None or num_div is None: print "Parameters are not correctly set for animation." sys.exit(1) if anime_type == 'arc' or anime_type is None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): self._set_dynamical_matrix() self._modulation = Modulation(self._dynamical_matrix, dimension, phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() def get_modulations(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulations() def get_delta_modulations(self): """Return modulations relative to equilibrium supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_delta_modulations() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() def set_irreps(self, q, is_little_cogroup=False, nac_q_direction=None, degeneracy_tolerance=1e-4): self._set_dynamical_matrix() self._irreps = IrReps( self._dynamical_matrix, q, is_little_cogroup=is_little_cogroup, nac_q_direction=nac_q_direction, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) def set_group_velocity(self, q_length=None): self._set_dynamical_matrix() self._group_velocity = GroupVelocity( self._dynamical_matrix, q_length=q_length, symmetry=self._primitive_symmetry, frequency_factor_to_THz=self._factor) def get_group_velocity(self): return self._group_velocity.get_group_velocity() def get_group_velocity_at_q(self, q_point): if self._group_velocity is None: self.set_group_velocity() self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] def _run_force_constants_from_forces(self, distributed_atom_list=None, decimals=None, computation_algorithm="svd"): if self._displacement_dataset is not None: self._force_constants = get_fc2( self._supercell, self._symmetry, self._displacement_dataset, atom_list=distributed_atom_list, decimals=decimals, computation_algorithm=computation_algorithm) def _set_dynamical_matrix(self): if self._nac_params is None: self._dynamical_matrix = DynamicalMatrix( self._supercell, self._primitive, self._force_constants, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) else: self._dynamical_matrix = DynamicalMatrixNAC( self._supercell, self._primitive, self._force_constants, nac_params=self._nac_params, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print ("Warning: point group symmetries of supercell and primitive" "cell are different.") def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_supercells_with_displacements(self): supercells = [] for disp in self._displacement_dataset['first_atoms']: positions = self._supercell.get_positions() positions[disp['number']] += disp['displacement'] supercells.append(Atoms( numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) if self._primitive_matrix is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix) self._primitive = get_primitive( self._supercell, trans_mat, self._symprec) num_satom = self._supercell.get_number_of_atoms() num_patom = self._primitive.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False
class Phonopy: def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, log_level=0): self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None if is_auto_displacements: self.generate_displacements(distance=distance) # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def set_post_process(self, primitive_matrix=None, sets_of_forces=None, displacement_dataset=None, force_constants=None, is_nac=None): print print( "********************************** Warning" "**********************************") print "set_post_process will be obsolete." print( " produce_force_constants is used instead of set_post_process" " for producing") print(" force constants from forces.") if primitive_matrix is not None: print( " primitive_matrix has to be given at Phonopy::__init__" " object creation.") print( "******************************************" "**********************************") print if primitive_matrix is not None: self._primitive_matrix = primitive_matrix self._build_primitive_cell() self._search_primitive_symmetry() if sets_of_forces is not None: self.set_forces(sets_of_forces) elif displacement_dataset is not None: self._displacement_dataset = displacement_dataset elif force_constants is not None: self.set_force_constants(force_constants) if self._displacement_dataset is not None: self.produce_force_constants() 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) def get_primitive(self): return self._primitive primitive = property(get_primitive) def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def set_supercell(self, supercell): self._supercell = supercell def get_symmetry(self): """return symmetry of supercell""" return self._symmetry symmetry = property(get_symmetry) def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def produce_force_constants(self, forces=None, calculate_full_force_constants=True, computation_algorithm="svd"): if forces is not None: self.set_forces(forces) if calculate_full_force_constants: self._run_force_constants_from_forces( decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self._run_force_constants_from_forces( distributed_atom_list=p2s_map, decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) def set_nac_params(self, nac_params=None, method=None): if method is not None: print "set_nac_params:" print " Keyword argument of \"method\" is not more supported." self._nac_params = nac_params def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset) def set_displacements(self, displacements): print print( "********************************** Warning" "**********************************") print "set_displacements is obsolete. Do nothing." print( "******************************************" "**********************************") print def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_displacement_dataset(self): return self._displacement_dataset def get_supercells_with_displacements(self): if self._displacement_dataset is None: return None else: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_forces(self, sets_of_forces): """ sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] """ for disp, forces in zip(self._displacement_dataset['first_atoms'], sets_of_forces): disp['forces'] = forces def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) def set_force_constants(self, force_constants): self._force_constants = force_constants def set_force_sets(self, force_sets): print print( "********************************** Warning" "**********************************") print "set_force_sets will be obsolete." print(" The method name is changed to set_displacement_dataset.") print( "******************************************" "**********************************") print self.set_displacement_dataset(force_sets) def set_displacement_dataset(self, displacement_dataset): """ displacement_dataset: {'natom': number_of_atoms_in_supercell, 'first_atoms': [ {'number': atom index of displaced atom, 'displacement': displacement in Cartesian coordinates, 'direction': displacement direction with respect to axes 'forces': forces on atoms in supercell}, {...}, ...]} """ self._displacement_dataset = displacement_dataset self._displacements = [] self._displacement_directions = [] for disp in self._displacement_dataset['first_atoms']: x = disp['displacement'] self._displacements.append([disp['number'], x[0], x[1], x[2]]) if 'direction' in disp: y = disp['direction'] self._displacement_directions.append( [disp['number'], y[0], y[1], y[2]]) if not self._displacement_directions: self._displacement_directions = None def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) def symmetrize_force_constants_by_space_group(self): rotations = self._symmetry.get_symmetry_operations()['rotations'] translations = self._symmetry.get_symmetry_operations()['translations'] set_tensor_symmetry(self._force_constants, self._supercell.get_cell().T, self._supercell.get_scaled_positions(), rotations, translations, self._symprec) def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def set_dynamical_matrix(self): self._set_dynamical_matrix() def get_dynamical_matrix_at_q(self, q): self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): self._set_dynamical_matrix() self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, symbols=None): return self._band_structure.plot_band(symbols) def write_yaml_band_structure(self): self._band_structure.write_yaml() def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_mesh_symmetry=True, is_eigenvectors=False, is_gamma_center=False): self._set_dynamical_matrix() self._mesh = Mesh( self._dynamical_matrix, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_mesh_symmetry, is_eigenvectors=is_eigenvectors, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor) def get_mesh(self): return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def write_yaml_mesh(self): self._mesh.write_yaml() def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, is_projection=False, band_indices=None, cutoff_frequency=None): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" return False else: tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, band_indices=band_indices, cutoff_frequency=cutoff_frequency) tp.set_thermal_properties(t_step=t_step, t_max=t_max, t_min=t_min) self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): return self._thermal_properties.plot_thermal_properties() def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) def set_partial_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False, direction=None): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if direction is not None: direction_cart = np.dot(direction, self._primitive.get_cell()) else: direction_cart = None pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method, direction=direction_cart) pdos.set_draw_area(freq_min, freq_max, freq_pitch) pdos.run() self._pdos = pdos def get_partial_DOS(self): """ Retern frequencies and partial_dos. The first element is freqs and the second is partial_dos. frequencies: [freq1, freq2, ...] partial_dos: [[atom1-freq1, atom1-freq2, ...], [atom2-freq1, atom2-freq2, ...], ...] """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): return self._pdos.plot_pdos(indices=pdos_indices, legend=legend) def write_partial_DOS(self): self._pdos.write() def set_total_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False): if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(freq_min, freq_max, freq_pitch) total_dos.run() self._total_dos = total_dos def get_total_DOS(self): """ Retern frequencies and total dos. The first element is freqs and the second is total dos. frequencies: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): return self._total_dos.plot_dos() def write_total_DOS(self): self._total_dos.write() def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, direction=None, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) td.run() self._thermal_displacements = td def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): return self._thermal_displacements.plot(is_legend) def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ if self._mesh is None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) tdm.set_temperature_range(t_min, t_max, t_step) tdm.run() self._thermal_displacement_matrices = tdm def get_thermal_displacement_matrices(self): if self._thermal_displacement_matrices is not None: return self._thermal_displacement_matrices.get_thermal_displacement_matrices( ) def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) td.run(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() def set_qpoints_phonon(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False, factor=VaspToTHz): self._set_dynamical_matrix() self._qpoints_phonon = QpointsPhonon( q_points, self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) def get_qpoints_phonon(self): return (self._qpoints_phonon.get_frequencies(), self._qpoints_phonon.get_eigenvectors()) def write_yaml_qpoints_phonon(self): self._qpoints_phonon.write_yaml() def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): self._set_dynamical_matrix() if q_point is None: animation = Animation([0, 0, 0], self._dynamical_matrix, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index is None or amplitude is None or num_div is None: print "Parameters are not correctly set for animation." sys.exit(1) if anime_type == 'arc' or anime_type is None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): self._set_dynamical_matrix() self._modulation = Modulation(self._dynamical_matrix, dimension, phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() def get_modulations(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulations() def get_delta_modulations(self): """Return modulations relative to equilibrium supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_delta_modulations() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() def set_irreps(self, q, is_little_cogroup=False, nac_q_direction=None, degeneracy_tolerance=1e-4): self._set_dynamical_matrix() self._irreps = IrReps(self._dynamical_matrix, q, is_little_cogroup=is_little_cogroup, nac_q_direction=nac_q_direction, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) def set_group_velocity(self, q_length=None): self._set_dynamical_matrix() self._group_velocity = GroupVelocity( self._dynamical_matrix, q_length=q_length, symmetry=self._primitive_symmetry, frequency_factor_to_THz=self._factor) def get_group_velocity(self): return self._group_velocity.get_group_velocity() def get_group_velocity_at_q(self, q_point): if self._group_velocity is None: self.set_group_velocity() self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] def _run_force_constants_from_forces(self, distributed_atom_list=None, decimals=None, computation_algorithm="svd"): if self._displacement_dataset is not None: self._force_constants = get_fc2( self._supercell, self._symmetry, self._displacement_dataset, atom_list=distributed_atom_list, decimals=decimals, computation_algorithm=computation_algorithm) def _set_dynamical_matrix(self): if self._nac_params is None: self._dynamical_matrix = DynamicalMatrix( self._supercell, self._primitive, self._force_constants, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) else: self._dynamical_matrix = DynamicalMatrixNAC( self._supercell, self._primitive, self._force_constants, nac_params=self._nac_params, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len( self._primitive_symmetry.get_pointgroup_operations())): print( "Warning: point group symmetries of supercell and primitive" "cell are different.") def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_supercells_with_displacements(self): supercells = [] for disp in self._displacement_dataset['first_atoms']: positions = self._supercell.get_positions() positions[disp['number']] += disp['displacement'] supercells.append( Atoms(numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) if self._primitive_matrix is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix) self._primitive = get_primitive(self._supercell, trans_mat, self._symprec) num_satom = self._supercell.get_number_of_atoms() num_patom = self._primitive.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False
band = [] for i in range(51): band.append(q_start + (q_end - q_start) / 50 * i) bands.append(band) dir_labels = [r'$\Gamma$', '$X$', '$W$', '$X$', '$K$', r'$\Gamma$', '$L$'] # plot the band structure clf() phonon.set_band_structure(bands) phonon.plot_band_structure(dir_labels) ylim(0, 20.) # save results to file band.yaml bs = BandStructure(bands, phonon.dynamical_matrix, phonon.primitive, factor=VaspToTHz) if write_yaml: bs.write_yaml() # add vertical lines to plot for p in bs.special_point: axvline(p, color='k') # Example showing how to load previously saved results if read_yaml: special_points, distances, frequencies = band_plot_from_file( gca(), 'band.yaml') # set x axis labels
class Phonopy: def __init__( self, unitcell, supercell_matrix, is_preprocess=True, distance=0.01, symprec=1e-5, factor=VaspToTHz, is_nosym=False, log_level=0 ): self.symprec = symprec self.unitcell = unitcell self.supercell_matrix = supercell_matrix self.distance = distance self.factor = factor self.is_nosym = is_nosym self.log_level = log_level self.supercell = None self.__supercell() self.symmetry = None self.__symmetry() if is_preprocess: self.displacements = None self.displacement_directions = None self.supercells_with_displacements = None self.set_displacements() # set_post_process self.primitive = None self.dynamical_matrix = None self.is_nac = False # set_force_constants or set_forces self.force_constants = None # set_band_structure self.__band_structure = None # set_mesh self.__mesh = None # set_thermal_properties self.__thermal_properties = None # set_thermal_displacements self.__thermal_displacements = None # set_partial_DOS self.__pdos = None # set_total_DOS self.__total_dos = None def __supercell(self): self.supercell = get_supercell( self.unitcell, self.supercell_matrix, self.symprec ) def get_supercell( self ): return self.supercell def set_supercell( self, supercell ): self.supercell = supercell def get_primitive( self ): return self.primitive def set_primitive( self, primitive ): self.primitive = primitive def __symmetry(self): self.symmetry = Symmetry( self.supercell, self.symprec, self.is_nosym ) def get_symmetry(self): return self.symmetry def set_displacements( self, is_plusminus='auto', is_diagonal=True ): """ displacements: List of displacements in Cartesian coordinates. See 'set_special_displacements' displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[ 0, 1, 0, 0 ], [ 7, 1, 0, 1 ], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ lattice = self.supercell.get_cell() self.displacements = [] self.displacement_directions = \ get_least_displacements( self.symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, log_level=self.log_level ) for disp in self.displacement_directions: atom_num = disp[0] disp_cartesian = np.dot(disp[1:], lattice) disp_cartesian *= self.distance / np.linalg.norm(disp_cartesian) self.displacements.append( [ atom_num, disp_cartesian[0], disp_cartesian[1], disp_cartesian[2] ] ) self.__supercells_with_displacements() def set_special_displacements(self, displacements): """ This method orverwrites displacements that were automatically determined in the post-process. displacemsts: List of disctionaries [[ 0, 0.01, 0.00, 0.00 ], ... ] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates """ self.displacements = displacements self.__supercells_with_displacements() def get_displacements( self ): return self.displacements def get_displacement_directions( self ): return self.displacement_directions def print_displacements(self): print "Least displacements:" print " Atom Displacement" print " ----------------------------" for disp in self.displacements: print " %4d " % disp[0], print disp[1:4] def __supercells_with_displacements(self): supercells = [] for disp in self.displacements: positions = self.supercell.get_positions() positions[disp[0]] += disp[1:4] supercells.append( Atoms( numbers = self.supercell.get_atomic_numbers(), masses = self.supercell.get_masses(), positions = positions, cell = self.supercell.get_cell(), pbc = True ) ) self.supercells_with_displacements = supercells def get_supercells_with_displacements(self): return self.supercells_with_displacements def set_post_process( self, primitive_matrix, set_of_forces=None, force_constants=None, is_nac=False ): """ Set forces to prepare phonon calculations. The order of 'set_of_forces' has to correspond to that of 'displacements'. 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 set_of_forces: [ [ [ f_1x, f_1y, f_1z ], [ f_2x, f_2y, f_2z ], ... ], # first supercell [ [ f_1x, f_1y, f_1z ], [ f_2x, f_2y, f_2z ], ... ], # second supercell ... ] """ self.is_nac = is_nac if not set_of_forces == None: self.set_forces( set_of_forces ) elif not force_constants == None: self.set_force_constants( force_constants ) elif self.force_constants == None: print "In set_post_process, set_of_forces or force_constants" print "has to be set." sys.exit(1) # Primitive cell inv_supercell_matrix = np.linalg.inv( self.supercell_matrix ) self.primitive = Primitive( self.supercell, np.dot( inv_supercell_matrix, primitive_matrix ), self.symprec ) # Dynamical Matrix if self.is_nac: self.dynamical_matrix = \ DynamicalMatrixNAC( self.supercell, self.primitive, self.force_constants, symprec=self.symprec ) else: self.dynamical_matrix = \ DynamicalMatrix( self.supercell, self.primitive, self.force_constants, symprec=self.symprec ) def set_nac_params( self, nac_params, method='wang' ): if self.is_nac: self.dynamical_matrix.set_nac_params( nac_params, method ) def get_dynamical_matrix( self ): return self.dynamical_matrix def set_forces( self, set_of_forces, is_tensor_symmetry=False ): # Forces forces = [] for i, disp in enumerate( self.displacements ): forces.append( Forces( disp[0], disp[1:4], set_of_forces[i] ) ) # Force constants self.force_constants = get_force_constants( forces, self.symmetry, self.supercell, is_tensor_symmetry ) def set_force_constants( self, force_constants ): self.force_constants = force_constants def symmetrize_force_constants( self, iteration=3 ): symmetrize_force_constants( self.force_constants, iteration ) def get_force_constants( self ): return self.force_constants def get_rotational_condition_of_fc( self ): return rotational_invariance( self.force_constants, self.supercell, self.primitive, self.symprec ) # Frequency at a q-point def get_frequencies(self, q): """ Calculate phonon frequency q: k-vector in reduced coordinates of primitive cell """ self.dynamical_matrix.set_dynamical_matrix(q) dm = self.dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm): if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self.factor # Band structure def set_band_structure( self, bands, is_eigenvectors=False ): self.__band_structure = BandStructure( bands, self.dynamical_matrix, self.primitive, is_eigenvectors=is_eigenvectors, factor=self.factor ) def get_band_structure( self ): band = self.__band_structure return ( band.get_distances(), band.get_qpoints(), band.get_eigenvalues(), band.get_eigenvectors() ) def plot_band_structure( self, symbols=None ): return self.__band_structure.plot_band( symbols ) def write_yaml_band_structure( self ): self.__band_structure.write_yaml() # Mesh sampling def set_mesh( self, mesh, shift=None, is_time_reversal=True, is_symmetry=True, is_eigenvectors=False ): self.__mesh = Mesh( self.dynamical_matrix, self.primitive, mesh, shift=shift, is_time_reversal=is_time_reversal, is_symmetry=is_symmetry, is_eigenvectors=is_eigenvectors, factor=self.factor, symprec=self.symprec ) def get_mesh( self ): return ( self.__mesh.get_weights(), self.__mesh.get_qpoints(), self.__mesh.get_eigenvalues(), self.__mesh.get_eigenvectors() ) def write_yaml_mesh( self ): self.__mesh.write_yaml() # Thermal property def set_thermal_properties( self, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None ): if self.__mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) tp = ThermalProperties( self.__mesh.get_eigenvalues(), weights=self.__mesh.get_weights(), factor=self.factor, cutoff_eigenvalue=cutoff_eigenvalue ) tp.set_thermal_properties( t_step, t_max, t_min ) self.__thermal_properties = tp def get_thermal_properties( self ): temps, fe, entropy, cv = \ self.__thermal_properties.get_thermal_properties() return np.array([ temps, fe, entropy, cv ]).transpose() def plot_thermal_properties( self ): return self.__thermal_properties.plot_thermal_properties() def write_yaml_thermal_properties( self ): self.__thermal_properties.write_yaml() # Partial DOS def set_partial_DOS( self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None ): if self.__mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self.__mesh.get_eigenvectors() == None: print "Eigenvectors have to be calculated." sys.exit(1) pdos = PartialDos( self.__mesh.get_eigenvalues(), self.__mesh.get_weights(), self.__mesh.get_eigenvectors(), factor=self.factor, sigma=sigma ) pdos.set_draw_area( omega_min, omega_max, omega_pitch ) pdos.calculate() self.__pdos = pdos def get_partial_DOS( self ): """ Retern omegas and partial_dos. The first element is omegas and the second is partial_dos. omegas: [ freq1, freq2, ... ] partial_dos: [[elem1-freq1, elem1-freq2, ... ], [elem2-freq1, elem2-freq2, ... ], ... ] where elem1: atom1-x compornent elem2: atom1-y compornent elem3: atom1-z compornent elem4: atom2-x compornent ... """ return self.__pdos.get_partial_dos() def plot_partial_DOS( self, pdos_indices ): return self.__pdos.plot_pdos( pdos_indices ) def write_partial_DOS( self ): self.__pdos.write() # Total DOS def set_total_DOS( self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None ): if self.__mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) total_dos = TotalDos( self.__mesh.get_eigenvalues(), self.__mesh.get_weights(), factor=self.factor, sigma=sigma ) total_dos.set_draw_area( omega_min, omega_max, omega_pitch ) total_dos.calculate() self.__total_dos = total_dos def get_total_DOS( self ): """ Retern omegas and total dos. The first element is omegas and the second is total dos. omegas: [ freq1, freq2, ... ] total_dos: [ dos1, dos2, ... ] """ return self.__total_dos.get_dos() def plot_total_DOS( self ): return self.__total_dos.plot_dos() def write_total_DOS( self ): self.__total_dos.write() # Thermal displacement def set_thermal_displacements( self, t_step=10, t_max=1000, t_min=0, projector=None, cutoff_eigenvalue=None ): """ cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz) """ if self.__mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self.__mesh.get_eigenvectors() == None: print "Eigenvectors have to be calculated." sys.exit(1) td = ThermalDisplacements( self.__mesh.get_eigenvalues(), self.__mesh.get_eigenvectors(), self.__mesh.get_weights(), self.primitive.get_masses(), factor=self.factor, cutoff_eigenvalue=cutoff_eigenvalue ) td.set_temperature_range( t_min, t_max, t_step ) if not projector==None: td.project_eigenvectors( projector, self.primitive.get_cell() ) td.set_thermal_displacements() self.__thermal_displacements = td def plot_thermal_displacements( self, is_legend=False ): return self.__thermal_displacements.plot_thermal_displacements( is_legend ) def write_yaml_thermal_displacements( self ): self.__thermal_displacements.write_yaml() # Thermal displacement def set_thermal_distances( self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None ): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [ [ 1, 2 ], [ 1, 4 ] ] cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz) """ td = ThermalDistances( self.__mesh.get_eigenvalues(), self.__mesh.get_eigenvectors(), self.__mesh.get_weights(), self.supercell, self.primitive, self.__mesh.get_qpoints(), symprec=self.symprec, factor=self.factor, cutoff_eigenvalue=cutoff_eigenvalue ) td.set_temperature_range( t_min, t_max, t_step ) td.set_thermal_distances( atom_pairs ) self.__thermal_distances = td def write_yaml_thermal_distances( self ): self.__thermal_distances.write_yaml() # Q-points mode def write_yaml_qpoints( self, qpoints, is_eigenvectors=False, factor=VaspToTHz ): write_yaml_qpoints( qpoints, self.primitive, self.dynamical_matrix, is_eigenvectors=is_eigenvectors, factor=self.factor ) # Animation def write_animation( self, qpoint=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None ): if qpoint==None: animation = Animation( [0, 0, 0], self.dynamical_matrix, self.primitive, shift=shift ) else: animation = Animation( qpoint, self.dynamical_matrix, self.primitive, shift=shift ) if anime_type=='v_sim': animation.write_v_sim( self.factor ) if ( anime_type=='arc' or anime_type=='xyz' or anime_type=='jmol' or anime_type=='poscar' ): if band_index==None or amplitude==None or num_div==None: print "Parameters are not correctly set for animation." sys.exit(1) if anime_type=='arc' or anime_type==None: animation.write_arc( band_index, amplitude, num_div ) if anime_type=='xyz': animation.write_xyz( band_index, amplitude, num_div, self.factor ) if anime_type=='jmol': animation.write_xyz_jmol( amplitude=amplitude, factor=self.factor ) if anime_type=='poscar': animation.write_POSCAR( band_index, amplitude, num_div ) def write_modulation( self, setting ): write_modulations( self.dynamical_matrix, self.primitive, setting )
class Phonopy: def __init__(self, unitcell, supercell_matrix, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, symprec=1e-5, is_symmetry=True, log_level=0): self._symprec = symprec self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._factor = factor self._is_symmetry = is_symmetry self._log_level = log_level self._supercell = None self._set_supercell() self._symmetry = None self._set_symmetry() # set_displacements (used only in preprocess) self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None if is_auto_displacements: self.generate_displacements(distance) # set_post_process self._primitive = None self._dynamical_matrix = None self._is_nac = False # set_force_constants or set_forces self._set_of_forces_objects = None self._force_constants = None # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def get_primitive(self): return self._primitive primitive = property(get_primitive) def set_primitive(self, primitive): self._primitive = primitive def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def set_supercell(self, supercell): self._supercell = supercell def get_symmetry(self): return self._symmetry symmetry = property(get_symmetry) def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacements: List of displacements in Cartesian coordinates. See 'set_displacements' displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ lattice = self._supercell.get_cell() self._displacements = [] self._displacement_directions = \ get_least_displacements(self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) for disp in self._displacement_directions: atom_num = disp[0] disp_cartesian = np.dot(disp[1:], lattice) disp_cartesian *= distance / np.linalg.norm(disp_cartesian) self._displacements.append([atom_num, disp_cartesian[0], disp_cartesian[1], disp_cartesian[2]]) self._set_supercells_with_displacements() def set_displacements(self, displacements): """Set displacements manually displacemsts: List of disctionaries [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates """ self._displacements = displacements self._set_supercells_with_displacements() def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_supercells_with_displacements(self): return self._supercells_with_displacements def set_post_process(self, primitive_matrix=np.eye(3, dtype=float), sets_of_forces=None, set_of_forces_objects=None, force_constants=None, is_nac=False, calculate_full_force_constants=False, force_constants_decimals=None, dynamical_matrix_decimals=None): """ Set forces or force constants to prepare phonon calculations. The order of 'sets_of_forces' has to correspond to that of 'displacements' that should be already stored. 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 sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] set_of_forces_objects: [FORCES_object, FORCES_object, FORCES_object, ...] """ self._is_nac = is_nac # Primitive cell inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) self._primitive = Primitive( self._supercell, np.dot(inv_supercell_matrix, primitive_matrix), self._symprec) # Set set of FORCES objects or force constants if sets_of_forces is not None: self.set_forces(sets_of_forces) elif set_of_forces_objects is not None: self.set_force_sets(set_of_forces_objects) elif force_constants is not None: self.set_force_constants(force_constants) # Calculate force cosntants from forces (full or symmetry reduced) if self._set_of_forces_objects is not None: if calculate_full_force_constants: self.set_force_constants_from_forces( distributed_atom_list=None, force_constants_decimals=force_constants_decimals) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self.set_force_constants_from_forces( distributed_atom_list=p2s_map, force_constants_decimals=force_constants_decimals) if self._force_constants is None: print "In set_post_process, sets_of_forces or force_constants" print "has to be set." return False # Dynamical Matrix self.set_dynamical_matrix(decimals=dynamical_matrix_decimals) def set_nac_params(self, nac_params, method='wang'): if self._is_nac: self._dynamical_matrix.set_nac_params(nac_params, method) def set_dynamical_matrix(self, decimals=None): if self._is_nac: self._dynamical_matrix = \ DynamicalMatrixNAC(self._supercell, self._primitive, self._force_constants, decimals=decimals, symprec=self._symprec) else: self._dynamical_matrix = \ DynamicalMatrix(self._supercell, self._primitive, self._force_constants, decimals=decimals, symprec=self._symprec) def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_forces(self, sets_of_forces): forces = [] for i, disp in enumerate(self._displacements): forces.append(Forces(disp[0], disp[1:4], sets_of_forces[i])) self._set_of_forces_objects = forces def set_force_constants_from_forces(self, distributed_atom_list=None, force_constants_decimals=None): self._force_constants = get_force_constants( self._set_of_forces_objects, self._symmetry, self._supercell, atom_list=distributed_atom_list, decimals=force_constants_decimals) def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) def set_force_constants(self, force_constants): self._force_constants = force_constants def set_force_sets(self, sets_of_forces_objects): self._set_of_forces_objects = sets_of_forces_objects def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def get_dynamical_matrix_at_q(self, q): self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() # Frequency at a q-point def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor # Frequency and eigenvector at a q-point def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors ## This expression may not be supported in old python versions. # frequencies = np.array( # [np.sqrt(x) if x > 0 else -np.sqrt(-x) for x in eigvals]) # return frequencies * self._factor, eigenvectors # Band structure def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): self._band_structure = BandStructure( bands, self._dynamical_matrix, self._primitive, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, symbols=None): return self._band_structure.plot_band(symbols) def write_yaml_band_structure(self): self._band_structure.write_yaml() # Mesh sampling def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_symmetry=True, is_band_connection=False, is_eigenvectors=False, is_gamma_center=False): self._mesh = Mesh(self._dynamical_matrix, self._primitive, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_symmetry, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, factor=self._factor, symprec=self._symprec) def get_mesh(self): return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def write_yaml_mesh(self): self._mesh.write_yaml() def write_hdf5_mesh(self): self._mesh.write_hdf5() # Thermal property def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, is_projection=False, cutoff_frequency=None): if self._mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, cutoff_frequency=cutoff_frequency) tp.set_thermal_properties(t_step, t_max, t_min) self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): return self._thermal_properties.plot_thermal_properties() def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) # Partial DOS def set_partial_DOS(self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None, tetrahedron_method=False): if self._mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) if self._mesh.get_eigenvectors() == None: print "Eigenvectors have to be calculated." sys.exit(1) pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) pdos.set_draw_area(omega_min, omega_max, omega_pitch) pdos.calculate() self._pdos = pdos def get_partial_DOS(self): """ Retern omegas and partial_dos. The first element is omegas and the second is partial_dos. omegas: [freq1, freq2, ...] partial_dos: [[elem1-freq1, elem1-freq2, ...], [elem2-freq1, elem2-freq2, ...], ...] where elem1: atom1-x compornent elem2: atom1-y compornent elem3: atom1-z compornent elem4: atom2-x compornent ... """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): return self._pdos.plot_pdos(indices=pdos_indices, legend=legend) def write_partial_DOS(self): self._pdos.write() # Total DOS def set_total_DOS(self, sigma=None, omega_min=None, omega_max=None, omega_pitch=None, tetrahedron_method=False): if self._mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(omega_min, omega_max, omega_pitch) total_dos.calculate() self._total_dos = total_dos def get_total_DOS(self): """ Retern omegas and total dos. The first element is omegas and the second is total dos. omegas: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): return self._total_dos.plot_dos() def write_total_DOS(self): self._total_dos.write() # Thermal displacement def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, direction=None, cutoff_eigenvalue=None): """ cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) direction: Projection direction in reduced coordinates """ if self._mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_eigenvalue=cutoff_eigenvalue) td.set_temperature_range(t_min, t_max, t_step) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) # td.run() td.run_mesh() self._thermal_displacements = td def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): return self._thermal_displacements.plot(is_legend) def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() def write_hdf5_thermal_displacements(self): self._thermal_displacements.write_hdf5() # Thermal displacement matrices def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None): """ cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) direction: Projection direction in reduced coordinates """ if self._mesh==None: print "set_mesh has to be done before set_thermal_properties" sys.exit(1) eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print "Eigenvectors have to be calculated." sys.exit(1) if np.prod(mesh_nums) != len(eigvecs): print "Sampling mesh must not be symmetrized." sys.exit(1) tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_eigenvalue=cutoff_eigenvalue) tdm.set_temperature_range(t_min, t_max, t_step) # tdm.run() tdm.run_mesh() self._thermal_displacement_matrices = tdm def get_thermal_displacement_matrices(self): if self._thermal_displacement_matrices is not None: return self._thermal_displacement_matrices.get_thermal_displacement_matrices() def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def write_hdf5_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_hdf5() # Thermal displacement def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_eigenvalue=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_eigenvalue: phonon modes that have frequencies below cutoff_eigenvalue are ignored. e.g. 0.1 (THz^2) """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), symprec=self._symprec, cutoff_eigenvalue=cutoff_eigenvalue) td.set_temperature_range(t_min, t_max, t_step) # td.run(atom_pairs) td.run_mesh(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() def write_hdf5_thermal_distances(self): self._thermal_distances.write_hdf5() # Q-points mode def write_yaml_qpoints(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False, factor=VaspToTHz): write_yaml_qpoints(q_points, self._primitive, self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) # Animation def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): if q_point==None: animation = Animation([0, 0, 0], self._dynamical_matrix, self._primitive, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, self._primitive, shift=shift) if anime_type=='v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type=='arc' or anime_type=='xyz' or anime_type=='jmol' or anime_type=='poscar'): if band_index==None or amplitude==None or num_div==None: print "Parameters are not correctly set for animation." sys.exit(1) if anime_type=='arc' or anime_type==None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type=='xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type=='jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type=='poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) # Modulation def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): self._modulation = Modulation(self._dynamical_matrix, self._primitive, dimension=dimension, phonon_modes=phonon_modes, delta_q= delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() def get_modulations(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulations() def get_delta_modulations(self): """Return modulations relative to equilibrium supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_delta_modulations() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() # Characters of irreducible representations def set_irreps(self, q, degeneracy_tolerance=1e-4): self._irreps = IrReps( self._dynamical_matrix, q, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) # Group velocity def set_group_velocity(self, q_points=None, q_length=1e-4): self._group_velocity = GroupVelocity( self._dynamical_matrix, q_points=q_points, symmetry=self._symmetry, q_length=q_length, frequency_factor_to_THz=self._factor) def get_group_velocity(self, q_point): self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] def _set_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _set_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _set_supercells_with_displacements(self): supercells = [] for disp in self._displacements: positions = self._supercell.get_positions() positions[disp[0]] += disp[1:4] supercells.append(Atoms( numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells
class Phonopy(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, nac_params=None, distance=None, factor=VaspToTHz, is_auto_displacements=None, dynamical_matrix_decimals=None, force_constants_decimals=None, symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): if is_auto_displacements is not None: print("Warning: \'is_auto_displacements\' argument is obsolete.") if is_auto_displacements is False: print("Sets of displacements are not created as default.") else: print("Use \'generate_displacements\' method explicitly to " "create sets of displacements.") if distance is not None: print("Warning: \'distance\' keyword argument is obsolete at " "Phonopy instantiation.") print("Specify \'distance\' keyword argument when calling " "\'generate_displacements\'") print("method (See the Phonopy API document).") self._symprec = symprec self._factor = factor self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = Atoms(atoms=unitcell) self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() # set_displacements (used only in preprocess) self._displacement_dataset = None self._displacements = None self._displacement_directions = None self._supercells_with_displacements = None # set_force_constants or set_forces self._force_constants = None self._force_constants_decimals = force_constants_decimals # set_dynamical_matrix self._dynamical_matrix = None self._nac_params = nac_params self._dynamical_matrix_decimals = dynamical_matrix_decimals # set_band_structure self._band_structure = None # set_mesh self._mesh = None # set_tetrahedron_method self._tetrahedron_method = None # set_thermal_properties self._thermal_properties = None # set_thermal_displacements self._thermal_displacements = None # set_thermal_displacement_matrices self._thermal_displacement_matrices = None # set_partial_DOS self._pdos = None # set_total_DOS self._total_dos = None # set_modulation self._modulation = None # set_character_table self._irreps = None # set_group_velocity self._group_velocity = None def get_version(self): return __version__ def get_primitive(self): return self._primitive primitive = property(get_primitive) def get_unitcell(self): return self._unitcell unitcell = property(get_unitcell) def get_supercell(self): return self._supercell supercell = property(get_supercell) def get_symmetry(self): """return symmetry of supercell""" return self._symmetry symmetry = property(get_symmetry) def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_supercell_matrix(self): return self._supercell_matrix def get_primitive_matrix(self): return self._primitive_matrix def get_unit_conversion_factor(self): return self._factor unit_conversion_factor = property(get_unit_conversion_factor) def get_displacement_dataset(self): return self._displacement_dataset def get_displacements(self): return self._displacements displacements = property(get_displacements) def get_displacement_directions(self): return self._displacement_directions displacement_directions = property(get_displacement_directions) def get_supercells_with_displacements(self): if self._displacement_dataset is None: return None else: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_force_constants(self): return self._force_constants force_constants = property(get_force_constants) def get_rotational_condition_of_fc(self): return rotational_invariance(self._force_constants, self._supercell, self._primitive, self._symprec) def get_nac_params(self): return self._nac_params def get_dynamical_matrix(self): return self._dynamical_matrix dynamical_matrix = property(get_dynamical_matrix) def set_unitcell(self, unitcell): self._unitcell = unitcell self._build_supercell() self._build_primitive_cell() self._search_symmetry() self._search_primitive_symmetry() self._displacement_dataset = None def set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._set_dynamical_matrix() def set_nac_params(self, nac_params=None): self._nac_params = nac_params self._set_dynamical_matrix() def set_displacement_dataset(self, displacement_dataset): """ displacement_dataset: {'natom': number_of_atoms_in_supercell, 'first_atoms': [ {'number': atom index of displaced atom, 'displacement': displacement in Cartesian coordinates, 'direction': displacement direction with respect to axes 'forces': forces on atoms in supercell}, {...}, ...]} """ self._displacement_dataset = displacement_dataset self._displacements = [] self._displacement_directions = [] for disp in self._displacement_dataset['first_atoms']: x = disp['displacement'] self._displacements.append([disp['number'], x[0], x[1], x[2]]) if 'direction' in disp: y = disp['direction'] self._displacement_directions.append( [disp['number'], y[0], y[1], y[2]]) if not self._displacement_directions: self._displacement_directions = None def set_forces(self, sets_of_forces): """ sets_of_forces: [[[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # first supercell [[f_1x, f_1y, f_1z], [f_2x, f_2y, f_2z], ...], # second supercell ... ] """ for disp, forces in zip( self._displacement_dataset['first_atoms'], sets_of_forces): disp['forces'] = forces def set_force_constants(self, force_constants): self._force_constants = force_constants self._set_dynamical_matrix() def set_force_constants_zero_with_radius(self, cutoff_radius): cutoff_force_constants(self._force_constants, self._supercell, cutoff_radius, symprec=self._symprec) self._set_dynamical_matrix() def set_dynamical_matrix(self): self._set_dynamical_matrix() def generate_displacements(self, distance=0.01, is_plusminus='auto', is_diagonal=True, is_trigonal=False): """Generate displacements automatically displacemsts: List of displacements in Cartesian coordinates. [[0, 0.01, 0.00, 0.00], ...] where each set of elements is defined by: First value: Atom index in supercell starting with 0 Second to fourth: Displacement in Cartesian coordinates displacement_directions: List of directions with respect to axes. This gives only the symmetrically non equivalent directions. The format is like: [[0, 1, 0, 0], [7, 1, 0, 1], ...] where each list is defined by: First value: Atom index in supercell starting with 0 Second to fourth: If the direction is displaced or not ( 1, 0, or -1 ) with respect to the axes. """ displacement_directions = get_least_displacements( self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal, is_trigonal=is_trigonal, log_level=self._log_level) displacement_dataset = direction_to_displacement( displacement_directions, distance, self._supercell) self.set_displacement_dataset(displacement_dataset) def produce_force_constants(self, forces=None, calculate_full_force_constants=True, computation_algorithm="svd"): if forces is not None: self.set_forces(forces) # A primitive check if 'forces' key is in displacement_dataset. for disp in self._displacement_dataset['first_atoms']: if 'forces' not in disp: return False if calculate_full_force_constants: self._run_force_constants_from_forces( decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) else: p2s_map = self._primitive.get_primitive_to_supercell_map() self._run_force_constants_from_forces( distributed_atom_list=p2s_map, decimals=self._force_constants_decimals, computation_algorithm=computation_algorithm) self._set_dynamical_matrix() return True def symmetrize_force_constants(self, iteration=3): symmetrize_force_constants(self._force_constants, iteration) self._set_dynamical_matrix() def symmetrize_force_constants_by_space_group(self): from phonopy.harmonic.force_constants import (set_tensor_symmetry, set_tensor_symmetry_PJ) set_tensor_symmetry_PJ(self._force_constants, self._supercell.get_cell().T, self._supercell.get_scaled_positions(), self._symmetry) self._set_dynamical_matrix() ##################### # Phonon properties # ##################### # Single q-point def get_dynamical_matrix_at_q(self, q): self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) return self._dynamical_matrix.get_dynamical_matrix() def get_frequencies(self, q): """ Calculate phonon frequencies at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] for eig in np.linalg.eigvalsh(dm).real: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor def get_frequencies_with_eigenvectors(self, q): """ Calculate phonon frequencies and eigenvectors at q q: q-vector in reduced coordinates of primitive cell """ self._set_dynamical_matrix() if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return None self._dynamical_matrix.set_dynamical_matrix(q) dm = self._dynamical_matrix.get_dynamical_matrix() frequencies = [] eigvals, eigenvectors = np.linalg.eigh(dm) frequencies = [] for eig in eigvals: if eig < 0: frequencies.append(-np.sqrt(-eig)) else: frequencies.append(np.sqrt(eig)) return np.array(frequencies) * self._factor, eigenvectors # Band structure def set_band_structure(self, bands, is_eigenvectors=False, is_band_connection=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._band_structure = None return False self._band_structure = BandStructure( bands, self._dynamical_matrix, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor) return True def get_band_structure(self): band = self._band_structure return (band.get_qpoints(), band.get_distances(), band.get_frequencies(), band.get_eigenvectors()) def plot_band_structure(self, labels=None): import matplotlib.pyplot as plt if labels: from matplotlib import rc rc('text', usetex=True) self._band_structure.plot(plt, labels=labels) return plt def write_yaml_band_structure(self, labels=None, comment=None, filename="band.yaml"): self._band_structure.write_yaml(labels=labels, comment=comment, filename=filename) # Sampling mesh def set_mesh(self, mesh, shift=None, is_time_reversal=True, is_mesh_symmetry=True, is_eigenvectors=False, is_gamma_center=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._mesh = None return False self._mesh = Mesh( self._dynamical_matrix, mesh, shift=shift, is_time_reversal=is_time_reversal, is_mesh_symmetry=is_mesh_symmetry, is_eigenvectors=is_eigenvectors, is_gamma_center=is_gamma_center, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor, use_lapack_solver=self._use_lapack_solver) return True def get_mesh(self): if self._mesh is None: return None else: return (self._mesh.get_qpoints(), self._mesh.get_weights(), self._mesh.get_frequencies(), self._mesh.get_eigenvectors()) def get_mesh_grid_info(self): if self._mesh is None: return None else: return (self._mesh.get_grid_address(), self._mesh.get_ir_grid_points(), self._mesh.get_grid_mapping_table()) def write_hdf5_mesh(self): self._mesh.write_hdf5() def write_yaml_mesh(self): self._mesh.write_yaml() # Plot band structure and DOS (PDOS) together def plot_band_structure_and_dos(self, pdos_indices=None, labels=None): import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec if labels: from matplotlib import rc rc('text', usetex=True) plt.figure(figsize=(10, 6)) gs = gridspec.GridSpec(1, 2, width_ratios=[3, 1]) ax1 = plt.subplot(gs[0, 0]) self._band_structure.plot(plt, labels=labels) ax2 = plt.subplot(gs[0, 1], sharey=ax1) plt.subplots_adjust(wspace=0.03) plt.setp(ax2.get_yticklabels(), visible=False) if pdos_indices is None: self._total_dos.plot(plt, ylabel="", draw_grid=False, flip_xy=True) else: self._pdos.plot(plt, indices=pdos_indices, ylabel="", draw_grid=False, flip_xy=True) return plt # DOS def set_total_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False): if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before DOS calculation.") self._total_dos = None return False total_dos = TotalDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method) total_dos.set_draw_area(freq_min, freq_max, freq_pitch) total_dos.run() self._total_dos = total_dos return True def get_total_DOS(self): """ Retern frequencies and total dos. The first element is freqs and the second is total dos. frequencies: [freq1, freq2, ...] total_dos: [dos1, dos2, ...] """ return self._total_dos.get_dos() def set_Debye_frequency(self, freq_max_fit=None): self._total_dos.set_Debye_frequency( self._primitive.get_number_of_atoms(), freq_max_fit=freq_max_fit) def get_Debye_frequency(self): return self._total_dos.get_Debye_frequency() def plot_total_DOS(self): import matplotlib.pyplot as plt self._total_dos.plot(plt) return plt def write_total_DOS(self): self._total_dos.write() # PDOS def set_partial_DOS(self, sigma=None, freq_min=None, freq_max=None, freq_pitch=None, tetrahedron_method=False, direction=None, xyz_projection=False): self._pdos = None if self._mesh is None: print("Warning: \'set_mesh\' has to be called before " "PDOS calculation.") return False if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False num_grid = np.prod(self._mesh.get_mesh_numbers()) if num_grid != len(self._mesh.get_ir_grid_points()): print("Warning: \'set_mesh\' has to be called with " "is_mesh_symmetry=False.") return False if direction is not None: direction_cart = np.dot(direction, self._primitive.get_cell()) else: direction_cart = None self._pdos = PartialDos(self._mesh, sigma=sigma, tetrahedron_method=tetrahedron_method, direction=direction_cart, xyz_projection=xyz_projection) self._pdos.set_draw_area(freq_min, freq_max, freq_pitch) self._pdos.run() return True def get_partial_DOS(self): """ Retern frequencies and partial_dos. The first element is freqs and the second is partial_dos. frequencies: [freq1, freq2, ...] partial_dos: [[atom1-freq1, atom1-freq2, ...], [atom2-freq1, atom2-freq2, ...], ...] """ return self._pdos.get_partial_dos() def plot_partial_DOS(self, pdos_indices=None, legend=None): import matplotlib.pyplot as plt self._pdos.plot(plt, indices=pdos_indices, legend=legend) return plt def write_partial_DOS(self): self._pdos.write() # Thermal property def set_thermal_properties(self, t_step=10, t_max=1000, t_min=0, temperatures=None, is_projection=False, band_indices=None, cutoff_frequency=None): if self._mesh is None: print("Warning: set_mesh has to be done before " "set_thermal_properties") return False else: tp = ThermalProperties(self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors(), is_projection=is_projection, band_indices=band_indices, cutoff_frequency=cutoff_frequency) if temperatures is None: tp.set_temperature_range(t_step=t_step, t_max=t_max, t_min=t_min) else: tp.set_temperatures(temperatures) tp.run() self._thermal_properties = tp def get_thermal_properties(self): temps, fe, entropy, cv = \ self._thermal_properties.get_thermal_properties() return temps, fe, entropy, cv def plot_thermal_properties(self): import matplotlib.pyplot as plt self._thermal_properties.plot(plt) return plt def write_yaml_thermal_properties(self, filename='thermal_properties.yaml'): self._thermal_properties.write_yaml(filename=filename) # Thermal displacement def set_thermal_displacements(self, t_step=10, t_max=1000, t_min=0, temperatures=None, direction=None, cutoff_frequency=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacements = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacements\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False td = ThermalDisplacements(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency) if temperatures is None: td.set_temperature_range(t_min, t_max, t_step) else: td.set_temperatures(temperatures) if direction is not None: td.project_eigenvectors(direction, self._primitive.get_cell()) td.run() self._thermal_displacements = td return True def get_thermal_displacements(self): if self._thermal_displacements is not None: return self._thermal_displacements.get_thermal_displacements() def plot_thermal_displacements(self, is_legend=False): import matplotlib.pyplot as plt self._thermal_displacements.plot(plt, is_legend=is_legend) return plt def write_yaml_thermal_displacements(self): self._thermal_displacements.write_yaml() # Thermal displacement matrix def set_thermal_displacement_matrices(self, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None, t_cif=None): """ cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. direction: Projection direction in reduced coordinates """ self._thermal_displacement_matrices = None if self._mesh is None: print("Warning: \'set_mesh\' has to finish correctly " "before \'set_thermal_displacement_matrices\'.") return False eigvecs = self._mesh.get_eigenvectors() frequencies = self._mesh.get_frequencies() mesh_nums = self._mesh.get_mesh_numbers() if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False if np.prod(mesh_nums) != len(eigvecs): print("Warning: Sampling mesh must not be symmetrized.") return False tdm = ThermalDisplacementMatrices(frequencies, eigvecs, self._primitive.get_masses(), cutoff_frequency=cutoff_frequency, lattice=self._primitive.get_cell().T) if t_cif is None: tdm.set_temperature_range(t_min, t_max, t_step) else: tdm.set_temperatures([t_cif]) tdm.run() self._thermal_displacement_matrices = tdm return True def get_thermal_displacement_matrices(self): tdm = self._thermal_displacement_matrices if tdm is not None: return tdm.get_thermal_displacement_matrices() def write_yaml_thermal_displacement_matrices(self): self._thermal_displacement_matrices.write_yaml() def write_thermal_displacement_matrix_to_cif(self, temperature_index): self._thermal_displacement_matrices.write_cif(self._primitive, temperature_index) # Mean square distance between a pair of atoms def set_thermal_distances(self, atom_pairs, t_step=10, t_max=1000, t_min=0, cutoff_frequency=None): """ atom_pairs: List of list Mean square distances are calculated for the atom_pairs e.g. [[1, 2], [1, 4]] cutoff_frequency: phonon modes that have frequencies below cutoff_frequency are ignored. """ td = ThermalDistances(self._mesh.get_frequencies(), self._mesh.get_eigenvectors(), self._supercell, self._primitive, self._mesh.get_qpoints(), cutoff_frequency=cutoff_frequency) td.set_temperature_range(t_min, t_max, t_step) td.run(atom_pairs) self._thermal_distances = td def write_yaml_thermal_distances(self): self._thermal_distances.write_yaml() # Sampling at q-points def set_qpoints_phonon(self, q_points, nac_q_direction=None, is_eigenvectors=False, write_dynamical_matrices=False): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._qpoints_phonon = None return False self._qpoints_phonon = QpointsPhonon( np.reshape(q_points, (-1, 3)), self._dynamical_matrix, nac_q_direction=nac_q_direction, is_eigenvectors=is_eigenvectors, group_velocity=self._group_velocity, write_dynamical_matrices=write_dynamical_matrices, factor=self._factor) return True def get_qpoints_phonon(self): return (self._qpoints_phonon.get_frequencies(), self._qpoints_phonon.get_eigenvectors()) def write_hdf5_qpoints_phonon(self): self._qpoints_phonon.write_hdf5() def write_yaml_qpoints_phonon(self): self._qpoints_phonon.write_yaml() # Normal mode animation def write_animation(self, q_point=None, anime_type='v_sim', band_index=None, amplitude=None, num_div=None, shift=None, filename=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") return False if q_point is None: animation = Animation([0, 0, 0], self._dynamical_matrix, shift=shift) else: animation = Animation(q_point, self._dynamical_matrix, shift=shift) if anime_type == 'v_sim': if amplitude: amplitude_ = amplitude else: amplitude_ = 1.0 if filename: animation.write_v_sim(amplitude=amplitude_, factor=self._factor, filename=filename) else: animation.write_v_sim(amplitude=amplitude_, factor=self._factor) if (anime_type == 'arc' or anime_type == 'xyz' or anime_type == 'jmol' or anime_type == 'poscar'): if band_index is None or amplitude is None or num_div is None: print("Warning: Parameters are not correctly set for " "animation.") return False if anime_type == 'arc' or anime_type is None: if filename: animation.write_arc(band_index, amplitude, num_div, filename=filename) else: animation.write_arc(band_index, amplitude, num_div) if anime_type == 'xyz': if filename: animation.write_xyz(band_index, amplitude, num_div, self._factor, filename=filename) else: animation.write_xyz(band_index, amplitude, num_div, self._factor) if anime_type == 'jmol': if filename: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor, filename=filename) else: animation.write_xyz_jmol(amplitude=amplitude, factor=self._factor) if anime_type == 'poscar': if filename: animation.write_POSCAR(band_index, amplitude, num_div, filename=filename) else: animation.write_POSCAR(band_index, amplitude, num_div) return True # Atomic modulation of normal mode def set_modulations(self, dimension, phonon_modes, delta_q=None, derivative_order=None, nac_q_direction=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._modulation = None return False self._modulation = Modulation(self._dynamical_matrix, dimension, phonon_modes, delta_q=delta_q, derivative_order=derivative_order, nac_q_direction=nac_q_direction, factor=self._factor) self._modulation.run() return True def get_modulated_supercells(self): """Returns cells with modulations as Atoms objects""" return self._modulation.get_modulated_supercells() def get_modulations_and_supercell(self): """Return modulations and supercell (modulations, supercell) modulations: Atomic modulations of supercell in Cartesian coordinates supercell: Supercell as an Atoms object. """ return self._modulation.get_modulations_and_supercell() def write_modulations(self): """Create MPOSCAR's""" self._modulation.write() def write_yaml_modulations(self): self._modulation.write_yaml() # Irreducible representation def set_irreps(self, q, is_little_cogroup=False, nac_q_direction=None, degeneracy_tolerance=1e-4): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._irreps = None return None self._irreps = IrReps( self._dynamical_matrix, q, is_little_cogroup=is_little_cogroup, nac_q_direction=nac_q_direction, factor=self._factor, symprec=self._symprec, degeneracy_tolerance=degeneracy_tolerance, log_level=self._log_level) return self._irreps.run() def get_irreps(self): return self._irreps def show_irreps(self, show_irreps=False): self._irreps.show(show_irreps=show_irreps) def write_yaml_irreps(self, show_irreps=False): self._irreps.write_yaml(show_irreps=show_irreps) # Group velocity def set_group_velocity(self, q_length=None): if self._dynamical_matrix is None: print("Warning: Dynamical matrix has not yet built.") self._group_velocity = None return False self._group_velocity = GroupVelocity( self._dynamical_matrix, q_length=q_length, symmetry=self._primitive_symmetry, frequency_factor_to_THz=self._factor) return True def get_group_velocity(self): return self._group_velocity.get_group_velocity() def get_group_velocity_at_q(self, q_point): if self._group_velocity is None: self.set_group_velocity() self._group_velocity.set_q_points([q_point]) return self._group_velocity.get_group_velocity()[0] # Moment def set_moment(self, order=1, is_projection=False, freq_min=None, freq_max=None): if self._mesh is None: print("Warning: set_mesh has to be done before set_moment") return False else: if is_projection: if self._mesh.get_eigenvectors() is None: print("Warning: Eigenvectors have to be calculated.") return False moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights(), eigenvectors=self._mesh.get_eigenvectors()) else: moment = PhononMoment( self._mesh.get_frequencies(), weights=self._mesh.get_weights()) if freq_min is not None or freq_max is not None: moment.set_frequency_range(freq_min=freq_min, freq_max=freq_max) moment.run(order=order) self._moment = moment.get_moment() return True def get_moment(self): return self._moment ################# # Local methods # ################# def _run_force_constants_from_forces(self, distributed_atom_list=None, decimals=None, computation_algorithm="svd"): if self._displacement_dataset is not None: self._force_constants = get_fc2( self._supercell, self._symmetry, self._displacement_dataset, atom_list=distributed_atom_list, decimals=decimals, computation_algorithm=computation_algorithm) def _set_dynamical_matrix(self): self._dynamical_matrix = None if (self._supercell is None or self._primitive is None): print("Bug: Supercell or primitive is not created.") return False elif self._force_constants is None: print("Warning: Force constants are not prepared.") return False elif self._primitive.get_masses() is None: print("Warning: Atomic masses are not correctly set.") return False else: if self._nac_params is None: self._dynamical_matrix = DynamicalMatrix( self._supercell, self._primitive, self._force_constants, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) else: self._dynamical_matrix = DynamicalMatrixNAC( self._supercell, self._primitive, self._force_constants, nac_params=self._nac_params, decimals=self._dynamical_matrix_decimals, symprec=self._symprec) return True def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: Point group symmetries of supercell and primitive" "cell are different.") def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_supercells_with_displacements(self): supercells = [] for disp in self._displacement_dataset['first_atoms']: positions = self._supercell.get_positions() positions[disp['number']] += disp['displacement'] supercells.append(Atoms( numbers=self._supercell.get_atomic_numbers(), masses=self._supercell.get_masses(), magmoms=self._supercell.get_magnetic_moments(), positions=positions, cell=self._supercell.get_cell(), pbc=True)) self._supercells_with_displacements = supercells def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ inv_supercell_matrix = np.linalg.inv(self._supercell_matrix) if self._primitive_matrix is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix) self._primitive = get_primitive( self._supercell, trans_mat, self._symprec) num_satom = self._supercell.get_number_of_atoms() num_patom = self._primitive.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False