def get_dynamical_matrix(fc2, supercell, primitive, nac_params=None, frequency_scale_factor=None, decimals=None, symprec=1e-5): if nac_params is None: dm = DynamicalMatrix( supercell, primitive, fc2, frequency_scale_factor=frequency_scale_factor, decimals=decimals, symprec=symprec) else: dm = DynamicalMatrixNAC( supercell, primitive, fc2, frequency_scale_factor=frequency_scale_factor, decimals=decimals, symprec=symprec) dm.set_nac_params(nac_params) return dm
def get_dynamical_matrix(fc2, supercell, primitive, nac_params=None, frequency_scale_factor=None, decimals=None, symprec=1e-5): if frequency_scale_factor is None: _fc2 = fc2 else: _fc2 = fc2 * frequency_scale_factor ** 2 if nac_params is None: dm = DynamicalMatrix( supercell, primitive, _fc2, decimals=decimals, symprec=symprec) else: dm = DynamicalMatrixNAC( supercell, primitive, _fc2, decimals=decimals, symprec=symprec) dm.set_nac_params(nac_params) return dm
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_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 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 _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 __init__(self, fc2, fc3, supercell, primitive, supercell_extra=None, nac_params=None, nac_q_direction=None, ion_clamped=False, factor=VaspToTHz, symprec=1e-5): self._fc2 = fc2 self._fc3 = fc3 self._scell = supercell if supercell_extra is not None: self._scell_extra = supercell_extra else: self._scell_extra = supercell self._pcell = primitive self._ion_clamped = ion_clamped self._factor = factor self._symprec = symprec if nac_params is None: self._dm = DynamicalMatrix(self._scell_extra, self._pcell, self._fc2, symprec=self._symprec) else: self._dm = DynamicalMatrixNAC(self._scell_extra, self._pcell, self._fc2, symprec=self._symprec) self._dm.set_nac_params(nac_params) self._nac_q_direction = nac_q_direction self._shortest_vectors, self._multiplicity = get_smallest_vectors( self._scell, self._pcell, self._symprec) self._shortest_vectors_extra, self._multiplicity_extra = get_smallest_vectors( self._scell_extra, self._pcell, self._symprec) if self._ion_clamped: num_atom_prim = self._pcell.get_number_of_atoms() self._X = np.zeros((num_atom_prim, 3, 3, 3), dtype=float) else: self._X = self._get_X() self._dPhidu = self._get_dPhidu() self._gruneisen_parameters = None self._frequencies = None self._qpoints = None self._mesh = None self._band_paths = None self._band_distances = None self._run_mode = None self._weights = None
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 _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) else: raise ValueError( 'Currently NAC is not available for unfolding.') return True
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, 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