def get_born_OUTCAR(poscar_filename="POSCAR", outcar_filename="OUTCAR", primitive_axis=np.eye(3), is_symmetry=True, symmetrize_tensors=False): cell = read_vasp(poscar_filename) primitive = Primitive(cell, primitive_axis) p2p = primitive.get_primitive_to_primitive_map() symmetry = Symmetry(primitive, is_symmetry=is_symmetry) independent_atoms = symmetry.get_independent_atoms() prim_lat = primitive.get_cell().T outcar = open(outcar_filename) borns = [] while True: line = outcar.readline() if not line: break if "NIONS" in line: num_atom = int(line.split()[11]) if "MACROSCOPIC STATIC DIELECTRIC TENSOR" in line: epsilon = [] outcar.readline() epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) if "BORN" in line: outcar.readline() line = outcar.readline() if "ion" in line: for i in range(num_atom): born = [] born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) outcar.readline() borns.append(born) reduced_borns = [] for p_i, u_i in enumerate(p2p): if p_i in independent_atoms: if symmetrize_tensors: site_sym = [similarity_transformation(prim_lat, rot) for rot in symmetry.get_site_symmetry(p_i)] reduced_borns.append(symmetrize_tensor(borns[u_i], site_sym)) else: reduced_borns.append(borns[u_i]) if symmetrize_tensors: point_sym = [similarity_transformation(prim_lat, rot) for rot in symmetry.get_pointgroup_operations()] epsilon = symmetrize_tensor(epsilon, point_sym) else: epsilon = np.array(epsilon) return np.array(reduced_borns), epsilon
def __init__(self, fc4, supercell, primitive, mesh, temperatures=None, band_indices=None, frequency_factor_to_THz=VaspToTHz, is_nosym=False, symprec=1e-3, cutoff_frequency=1e-4, log_level=False, lapack_zheev_uplo='L'): self._fc4 = fc4 self._supercell = supercell self._primitive = primitive self._masses = np.double(self._primitive.get_masses()) self._mesh = np.intc(mesh) if temperatures is None: self._temperatures = np.double([0]) else: self._temperatures = np.double(temperatures) num_band = primitive.get_number_of_atoms() * 3 if band_indices is None: self._band_indices = np.arange(num_band, dtype='intc') else: self._band_indices = np.intc(band_indices) self._frequency_factor_to_THz = frequency_factor_to_THz self._is_nosym = is_nosym self._symprec = symprec self._cutoff_frequency = cutoff_frequency self._log_level = log_level self._lapack_zheev_uplo = lapack_zheev_uplo symmetry = Symmetry(primitive, symprec=symprec) self._point_group_operations = symmetry.get_pointgroup_operations() self._grid_address = None self._bz_map = None self._set_grid_address() self._grid_point = None self._quartets_at_q = None self._weights_at_q = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._dm = None self._nac_q_direction = None self._frequency_shifts = None # Unit to THz of Delta self._unit_conversion = (EV / Angstrom ** 4 / AMU ** 2 / (2 * np.pi * THz) ** 2 * Hbar * EV / (2 * np.pi * THz) / 8 / np.prod(self._mesh)) self._allocate_phonon()
def get_born_OUTCAR( poscar_filename="POSCAR", outcar_filename="OUTCAR", primitive_axis=np.eye(3), supercell_matrix=np.eye(3, dtype="intc"), is_symmetry=True, symmetrize_tensors=False, symprec=1e-5, ): ucell = read_vasp(poscar_filename) outcar = open(outcar_filename) borns, epsilon = _read_born_and_epsilon(outcar) num_atom = len(borns) assert num_atom == ucell.get_number_of_atoms() if symmetrize_tensors: lattice = ucell.get_cell().T positions = ucell.get_scaled_positions() u_sym = Symmetry(ucell, is_symmetry=is_symmetry, symprec=symprec) point_sym = [similarity_transformation(lattice, r) for r in u_sym.get_pointgroup_operations()] epsilon = _symmetrize_tensor(epsilon, point_sym) borns = _symmetrize_borns(borns, u_sym, lattice, positions, symprec) inv_smat = np.linalg.inv(supercell_matrix) scell = get_supercell(ucell, supercell_matrix, symprec=symprec) pcell = get_primitive(scell, np.dot(inv_smat, primitive_axis), symprec=symprec) p2s = np.array(pcell.get_primitive_to_supercell_map(), dtype="intc") p_sym = Symmetry(pcell, is_symmetry=is_symmetry, symprec=symprec) s_indep_atoms = p2s[p_sym.get_independent_atoms()] u2u = scell.get_unitcell_to_unitcell_map() u_indep_atoms = [u2u[x] for x in s_indep_atoms] reduced_borns = borns[u_indep_atoms].copy() return reduced_borns, epsilon
def __init__(self, fc4, supercell, primitive, mesh, temperatures=None, band_indices=None, frequency_factor_to_THz=VaspToTHz, is_nosym=False, symprec=1e-3, cutoff_frequency=1e-4, log_level=False, lapack_zheev_uplo='L'): self._fc4 = fc4 self._supercell = supercell self._primitive = primitive self._masses = np.double(self._primitive.get_masses()) self._mesh = np.intc(mesh) if temperatures is None: self._temperatures = np.double([0]) else: self._temperatures = np.double(temperatures) num_band = primitive.get_number_of_atoms() * 3 if band_indices is None: self._band_indices = np.arange(num_band, dtype='intc') else: self._band_indices = np.intc(band_indices) self._frequency_factor_to_THz = frequency_factor_to_THz self._is_nosym = is_nosym self._symprec = symprec self._cutoff_frequency = cutoff_frequency self._log_level = log_level self._lapack_zheev_uplo = lapack_zheev_uplo symmetry = Symmetry(primitive, symprec=symprec) self._point_group_operations = symmetry.get_pointgroup_operations() self._grid_address = None self._bz_map = None self._set_grid_address() self._grid_point = None self._quartets_at_q = None self._weights_at_q = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._dm = None self._nac_q_direction = None self._frequency_shifts = None # Unit to THz of Delta self._unit_conversion = (EV / Angstrom**4 / AMU**2 / (2 * np.pi * THz)**2 * Hbar * EV / (2 * np.pi * THz) / 8 / np.prod(self._mesh)) self._allocate_phonon()
def get_number_of_triplets(primitive, mesh, grid_point, symprec=1e-5): mesh = np.array(mesh, dtype='intc') symmetry = Symmetry(primitive, symprec) point_group = symmetry.get_pointgroup_operations() primitive_lattice = np.linalg.inv(primitive.get_cell()) triplets_at_q, _, _, _, _, _ = get_triplets_at_q(grid_point, mesh, point_group, primitive_lattice) return len(triplets_at_q)
def get_coarse_ir_grid_points(primitive, mesh, mesh_divisors, coarse_mesh_shifts, is_kappa_star=True, symprec=1e-5): mesh = np.array(mesh, dtype='intc') symmetry = Symmetry(primitive, symprec) point_group = symmetry.get_pointgroup_operations() if mesh_divisors is None: (ir_grid_points, ir_grid_weights, grid_address, grid_mapping_table) = get_ir_grid_points(mesh, point_group) else: mesh_divs = np.array(mesh_divisors, dtype='intc') coarse_mesh = mesh // mesh_divs if coarse_mesh_shifts is None: coarse_mesh_shifts = [False, False, False] if not is_kappa_star: coarse_grid_address = get_grid_address(coarse_mesh) coarse_grid_points = np.arange(np.prod(coarse_mesh), dtype='intc') coarse_grid_weights = np.ones(len(coarse_grid_points), dtype='intc') else: (coarse_ir_grid_points, coarse_ir_grid_weights, coarse_grid_address, coarse_grid_mapping_table) = get_ir_grid_points( coarse_mesh, point_group, mesh_shifts=coarse_mesh_shifts) ir_grid_points = from_coarse_to_dense_grid_points( mesh, mesh_divs, coarse_grid_points, coarse_grid_address, coarse_mesh_shifts=coarse_mesh_shifts) grid_address = get_grid_address(mesh) ir_grid_weights = ir_grid_weights primitive_lattice = np.linalg.inv(primitive.get_cell()) bz_grid_address, bz_map = spg.relocate_BZ_grid_address(grid_address, mesh, primitive_lattice) return (ir_grid_points, ir_grid_weights, bz_grid_address, grid_mapping_table)
def get_coarse_ir_grid_points(primitive, mesh, mesh_divisors, coarse_mesh_shifts, is_kappa_star=True, symprec=1e-5): mesh = np.array(mesh, dtype='intc') symmetry = Symmetry(primitive, symprec) point_group = symmetry.get_pointgroup_operations() if mesh_divisors is None: (ir_grid_points, ir_grid_weights, grid_address, grid_mapping_table) = get_ir_grid_points(mesh, point_group) else: mesh_divs = np.array(mesh_divisors, dtype='intc') coarse_mesh = mesh // mesh_divs if coarse_mesh_shifts is None: coarse_mesh_shifts = [False, False, False] if not is_kappa_star: coarse_grid_address = get_grid_address(coarse_mesh) coarse_grid_points = np.arange(np.prod(coarse_mesh), dtype='uintp') else: (coarse_ir_grid_points, coarse_ir_grid_weights, coarse_grid_address, coarse_grid_mapping_table) = get_ir_grid_points( coarse_mesh, point_group, mesh_shifts=coarse_mesh_shifts) ir_grid_points = from_coarse_to_dense_grid_points( mesh, mesh_divs, coarse_grid_points, coarse_grid_address, coarse_mesh_shifts=coarse_mesh_shifts) grid_address = get_grid_address(mesh) ir_grid_weights = ir_grid_weights reciprocal_lattice = np.linalg.inv(primitive.get_cell()) bz_grid_address, bz_map = spg.relocate_BZ_grid_address(grid_address, mesh, reciprocal_lattice, is_dense=True) return (ir_grid_points, ir_grid_weights, bz_grid_address, grid_mapping_table)
def get_number_of_triplets(primitive, mesh, grid_point, symprec=1e-5): mesh = np.array(mesh, dtype='intc') symmetry = Symmetry(primitive, symprec) point_group = symmetry.get_pointgroup_operations() primitive_lattice = np.linalg.inv(primitive.get_cell()) triplets_at_q, _, _, _, _, _ = get_triplets_at_q( grid_point, mesh, point_group, primitive_lattice) return len(triplets_at_q)
def get_born_OUTCAR(poscar_filename="POSCAR", outcar_filename="OUTCAR", primitive_matrix=None, supercell_matrix=None, is_symmetry=True, symmetrize_tensors=False, symprec=1e-5): if primitive_matrix is None: pmat = np.eye(3) else: pmat = primitive_matrix if supercell_matrix is None: smat = np.eye(3, dtype='intc') else: smat = supercell_matrix ucell = read_vasp(poscar_filename) outcar = open(outcar_filename) borns, epsilon = _read_born_and_epsilon(outcar) num_atom = len(borns) assert num_atom == ucell.get_number_of_atoms() if symmetrize_tensors: lattice = ucell.get_cell().T positions = ucell.get_scaled_positions() u_sym = Symmetry(ucell, is_symmetry=is_symmetry, symprec=symprec) point_sym = [ similarity_transformation(lattice, r) for r in u_sym.get_pointgroup_operations() ] epsilon = _symmetrize_tensor(epsilon, point_sym) borns = _symmetrize_borns(borns, u_sym, lattice, positions, symprec) inv_smat = np.linalg.inv(smat) scell = get_supercell(ucell, smat, symprec=symprec) pcell = get_primitive(scell, np.dot(inv_smat, pmat), symprec=symprec) p2s = np.array(pcell.get_primitive_to_supercell_map(), dtype='intc') p_sym = Symmetry(pcell, is_symmetry=is_symmetry, symprec=symprec) s_indep_atoms = p2s[p_sym.get_independent_atoms()] u2u = scell.get_unitcell_to_unitcell_map() u_indep_atoms = [u2u[x] for x in s_indep_atoms] reduced_borns = borns[u_indep_atoms].copy() return reduced_borns, epsilon, s_indep_atoms
def get_coarse_ir_grid_points(primitive, mesh, mesh_divisors, coarse_mesh_shifts, is_nosym=False, symprec=1e-5): if mesh_divisors is None: mesh_divs = [1, 1, 1] else: mesh_divs = mesh_divisors mesh = np.array(mesh, dtype='intc') mesh_divs = np.array(mesh_divs, dtype='intc') coarse_mesh = mesh // mesh_divs if coarse_mesh_shifts is None: coarse_mesh_shifts = [False, False, False] if is_nosym: coarse_grid_address = get_grid_address(coarse_mesh) coarse_grid_points = np.arange(np.prod(coarse_mesh), dtype='intc') coarse_grid_weights = np.ones(len(coarse_grid_points), dtype='intc') else: symmetry = Symmetry(primitive, symprec) (coarse_grid_points, coarse_grid_weights, coarse_grid_address) = get_ir_grid_points( coarse_mesh, symmetry.get_pointgroup_operations(), mesh_shifts=coarse_mesh_shifts) grid_points = from_coarse_to_dense_grid_points( mesh, mesh_divs, coarse_grid_points, coarse_grid_address, coarse_mesh_shifts=coarse_mesh_shifts) grid_address = get_grid_address(mesh) primitive_lattice = np.linalg.inv(primitive.get_cell()) bz_grid_address, _ = spg.relocate_BZ_grid_address(grid_address, mesh, primitive_lattice) return grid_points, coarse_grid_weights, bz_grid_address
def get_coarse_ir_grid_points(primitive, mesh, mesh_divisors, coarse_mesh_shifts, is_nosym=False, symprec=1e-5): if mesh_divisors is None: mesh_divs = [1, 1, 1] else: mesh_divs = mesh_divisors mesh = np.array(mesh, dtype='intc') mesh_divs = np.array(mesh_divs, dtype='intc') coarse_mesh = mesh / mesh_divs if coarse_mesh_shifts is None: coarse_mesh_shifts = [False, False, False] if is_nosym: coarse_grid_address = get_grid_address(coarse_mesh) coarse_grid_points = np.arange(np.prod(coarse_mesh), dtype='intc') coarse_grid_weights = np.ones(len(coarse_grid_points), dtype='intc') else: symmetry = Symmetry(primitive, symprec) (coarse_grid_points, coarse_grid_weights, coarse_grid_address) = get_ir_grid_points( coarse_mesh, symmetry.get_pointgroup_operations(), mesh_shifts=coarse_mesh_shifts) grid_points = from_coarse_to_dense_grid_points( mesh, mesh_divs, coarse_grid_points, coarse_grid_address, coarse_mesh_shifts=coarse_mesh_shifts) grid_address = get_grid_address(mesh) primitive_lattice = np.linalg.inv(primitive.get_cell()) bz_grid_address, _ = spg.relocate_BZ_grid_address(grid_address, mesh, primitive_lattice) return grid_points, coarse_grid_weights, bz_grid_address
def set_grid_point(self, grid_point, grid_points2=None, weights2=None): "The grid_point is numbered using the bz scheme" self._grid_point = grid_point if self._grid_address is None: primitive_lattice = np.linalg.inv(self._primitive.get_cell()) self._grid_address, self._bz_map, self._bz_to_pp_map = get_bz_grid_address( self._mesh, primitive_lattice, with_boundary=True, is_bz_map_to_pp=True) if grid_points2 is not None: self._grid_points2 = grid_points2 self._weights2 = weights2 elif not self._is_nosym: symmetry = Symmetry(self._primitive, symprec=self._symprec) qpoint = self._grid_address[grid_point] / np.double(self._mesh) mapping, grid_points = spg.get_stabilized_reciprocal_mesh( self._mesh, symmetry.get_pointgroup_operations(), is_shift=np.zeros(3, dtype='intc'), is_time_reversal=True, qpoints=[qpoint]) grid_points2 = np.unique(mapping) # weight = np.zeros_like(grid_points2) # bz_grid_points2 = np.zeros_like(grid_points2) # for g, grid in enumerate(grid_points2): # weight[g] = len(np.where(grid == mapping)[0]) # bz_grid_points2[g] = np.where(grid == self._bz_to_pp_map)[0][0] # self._grid_points2 = bz_grid_points2 weight_temp = np.bincount(mapping) weight = weight_temp[np.nonzero(weight_temp)] self._grid_points2 = grid_points2 self._weights2 = weight else: self._grid_points2 = np.arange(np.prod(self._mesh)) self._weights2 = np.ones_like(self._grid_points2, dtype="intc")
def set_grid_point(self, grid_point, grid_points2=None, weights2 = None): "The grid_point is numbered using the bz scheme" self._grid_point = grid_point if self._grid_address is None: primitive_lattice = np.linalg.inv(self._primitive.get_cell()) self._grid_address, self._bz_map, self._bz_to_pp_map = get_bz_grid_address( self._mesh, primitive_lattice, with_boundary=True, is_bz_map_to_pp=True) if grid_points2 is not None: self._grid_points2 = grid_points2 self._weights2 = weights2 elif not self._is_nosym: symmetry = Symmetry(self._primitive, symprec=self._symprec) qpoint = self._grid_address[grid_point] / np.double(self._mesh) mapping, grid_points = spg.get_stabilized_reciprocal_mesh(self._mesh, symmetry.get_pointgroup_operations(), is_shift=np.zeros(3, dtype='intc'), is_time_reversal=True, qpoints=[qpoint]) grid_points2 = np.unique(mapping) # weight = np.zeros_like(grid_points2) # bz_grid_points2 = np.zeros_like(grid_points2) # for g, grid in enumerate(grid_points2): # weight[g] = len(np.where(grid == mapping)[0]) # bz_grid_points2[g] = np.where(grid == self._bz_to_pp_map)[0][0] # self._grid_points2 = bz_grid_points2 weight_temp = np.bincount(mapping) weight = weight_temp[np.nonzero(weight_temp)] self._grid_points2 = grid_points2 self._weights2 = weight else: self._grid_points2 = np.arange(np.prod(self._mesh)) self._weights2 = np.ones_like(self._grid_points2, dtype="intc")
class Phono3py: def __init__(self, interaction, mass_variances=None, length=None, adaptive_sigma_step = 0, frequency_factor_to_THz=None, is_nosym=False, symprec=1e-5, log_level=0, is_thm = False, # is tetrahedron method write_tecplot=False): self._interaction = interaction self._primitive = interaction.get_primitive() self._supercell = interaction.get_supercell() self._mesh = interaction.get_mesh_numbers() self._band_indices = interaction.get_band_indices() self._frequency_factor_to_THz = frequency_factor_to_THz self._is_nosym = is_nosym self._is_symmetry = not is_nosym self._symprec = symprec self._log_level = log_level self._asigma_step=adaptive_sigma_step self._step=0 self._kappa = None self._gamma = None self._br = None self._is_thm = is_thm self._write_tecplot=write_tecplot self._mass_variances = mass_variances self._length=length self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry 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 set_dynamical_matrix(self, fc2, supercell, primitive, nac_params=None, nac_q_direction=None, frequency_scale_factor=None): self._interaction.set_dynamical_matrix( fc2, supercell, primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) def get_imag_self_energy(self, grid_points, frequency_step=1.0, sigmas=[None], temperatures=[0.0], filename=None): ise = ImagSelfEnergy(self._interaction, is_thm = self._is_thm) for gp in grid_points: ise.set_grid_point(gp) ise.run_interaction() for sigma in sigmas: ise.set_sigma(sigma) for t in temperatures: ise.set_temperature(t) max_freq = (np.amax(self._interaction.get_phonons()[0]) * 2 + sigma * 4) fpoints = np.arange(0, max_freq + frequency_step / 2, frequency_step) ise.set_fpoints(fpoints) ise.run() gamma = ise.get_imag_self_energy() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_damping_functions( gp, bi, self._mesh, fpoints, gamma[:, pos:(pos + len(bi))].sum(axis=1) / len(bi), sigma=sigma, temperature=t, filename=filename) def get_matrix_contribution(self, grid_points, frequency_step=1.0, sigmas=[0.1], is_adaptive_sigma=False, filename=None): mc = Collision(self._interaction, sigmas=sigmas, is_adaptive_sigma=is_adaptive_sigma, frequencies = self._frequencies, degeneracy=self._degeneracy, write=write_scr, read=read_scr, cutoff_frequency=self._cutfr, cutoff_lifetime=self._cutlt) is_sum = False if grid_points == None: is_sum = True if self._is_nosym: # All grid points grid_points = np.arange(np.product(self._mesh)) else: # Automatic sampling (grid_points, grid_weights, grid_address) = get_ir_grid_points( self._mesh, self._primitive) matrix_contributions = [] for gp in grid_points: print "# grid %d" %gp mc.set_grid_point(gp) mc.run_interaction() matrix_contributions_temp = [] for sigma in sigmas: mc.set_sigma(sigma) max_freq = (np.amax(self._interaction.get_phonons()[0]) * 2 + sigma * 4) fpoints = np.arange(0, max_freq + frequency_step / 2, frequency_step) mc.set_fpoints(fpoints) mc.run() matrix_contribution = mc.get_imag_self_energy() matrix_contributions_temp.append(matrix_contribution) if not is_sum: write_matrix_contribution( gp, self._mesh, fpoints, matrix_contribution, sigma=sigma, filename=filename) matrix_contributions.append(matrix_contributions_temp) matrix_contributions = np.array(matrix_contributions) if is_sum: for i, sigma in enumerate(sigmas): write_matrix_contribution( None, self._mesh, fpoints, np.sum(matrix_contributions[:,i], axis=0), sigma=sigma, filename=filename) def get_decay_channels(self, grid_points, sets_of_band_indices, sigmas=[0.2], nu=None, scattering_class=None, read_amplitude=False, temperature=None, filename=None): self._interaction.set_is_disperse(True) if grid_points==None: print "Grid points are not specified." return False if sets_of_band_indices==None: print "Band indices are not specified." return False self._read_amplitude = read_amplitude decay=DecayChannel(self._interaction, nu=nu, scattering_class=scattering_class) for gp in grid_points: decay.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Decay channel ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) # decay.run_interaction() for sigma in sigmas: decay.set_sigma(sigma) decay.run_interaction() for i, t in enumerate(temperature): decay.set_temperature(t) decay.get_decay_channels(filename = filename) def get_linewidth(self, grid_points, sigmas=[0.1], temperatures=None, read_amplitude=False, nu=None, scattering_class=None, band_paths=None, filename=None): self._interaction.set_is_disperse(True) ise = ImagSelfEnergy(self._interaction, nu=nu) if temperatures is None: temperatures = np.arange(0, 1000.0 + 10.0 / 2.0, 10.0) self._temperatures=temperatures self._read_amplitude=read_amplitude if grid_points is not None: self.get_linewidth_at_grid_points(ise, sigmas, temperatures, grid_points, nu, scattering_class, filename) elif band_paths is not None: self.get_linewidth_at_paths(ise, sigmas, temperatures, band_paths, nu, scattering_class, filename) def get_linewidth_at_paths(self, ise, sigmas, temperatures, band_paths, is_nu, scattering_class=None, filename=None): if self._is_nosym: grid_address, grid_mapping =get_grid_address([x + (x % 2 == 0) for x in self._mesh], is_return_map=True) else: grids, weights, grid_address, grid_mapping = get_ir_grid_points([x + (x % 2 == 0) for x in self._mesh], self._primitive, is_return_map=True) qpoints_address=grid_address / np.array(self._mesh, dtype=float) print "Paths in reciprocal reduced coordinates:" for sigma in sigmas: self._sigma = sigma lws_paths=[] distances=[] frequencies=[] new_paths = [] for path in band_paths: print "[%5.2f %5.2f %5.2f] --> [%5.2f %5.2f %5.2f]" % (tuple(path[0]) + tuple(path[-1])) (new_path, dists, freqs, lws)=self.path(path, qpoints_address, grid_mapping, ise, temperatures, scattering_class) new_paths.append(new_path) distances.append(dists) frequencies.append(freqs) lws_paths.append(lws) self._distances=distances self._freqs_on_path=frequencies self._lws_on_path=lws_paths write_linewidth_band_csv(self._mesh, new_paths, distances=self._distances, lws=self._lws_on_path, band_indices=self._band_indices, temperatures=temperatures, frequencies=self._freqs_on_path, scattering_class=scattering_class, nu=is_nu, filename=(("-s%s"%sigma) if sigma is not None else "")) self.plot_lw() def plot_lw(self, symbols=None): import matplotlib.pyplot as plt if symbols: from matplotlib import rc rc('text', usetex=True) distances=np.array(sum(np.array(self._distances).tolist(), [])) special_point=[0.0] + [d[-1] for d in self._distances] lws=np.concatenate(tuple(self._lws_on_path), axis=0) for t in np.arange(lws.shape[1]): for b in np.arange(lws.shape[2]): plt.figure() plt.plot(distances, lws[:,t,b]) plt.ylabel('Linewidth') plt.xlabel('Wave vector') if symbols and len(symbols)==len(special_point): plt.xticks(special_point, symbols) else: plt.xticks(special_point, [''] * len(special_point)) plt.xlim(0, special_point[-1]) plt.axhline(y=0, linestyle=':', linewidth=0.5, color='b') if len(lws.shape)== 4: plt.legend(["Total","Normal process", "Umklapp process"]) plt.savefig("linewidth-t%d-b%d.pdf"%(self._temperatures[t],self._band_indices[b]+1)) plt.show() def path(self, path, qpoints_address, grids_mapping, ise, temperatures, scattering_class=None): lw_on_path=np.zeros((len(path),len(temperatures), len(self._band_indices)), dtype="double") freqs_on_path=np.zeros((len(path), len(self._band_indices)), dtype="double") new_path = [] for p, qpoint in enumerate(path): # dist_vector=np.abs(qpoints_address-qpoint) distance= np.sqrt(np.sum((qpoints_address-qpoint) ** 2, axis=-1)) grid=np.argmin(distance) ise.set_grid_point(grids_mapping[grid]) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Linewidth ------(%d/%d)"%(p, len(path)) print "calculated at qpoint", qpoint print "and represented by grid: %d (%.7f %.7f %.7f) due to the finite mesh" % (grid, qpoints_address[grid][0], qpoints_address[grid][1], qpoints_address[grid][2]) print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) new_path.append(qpoints_address[grid]) ise.set_sigma(self._sigma) ise.run_interaction(scattering_class, self._read_amplitude) freqs_on_path[p]=self._interaction._frequencies[grids_mapping[grid]][self._band_indices] gamma = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') for i, t in enumerate(temperatures): ise.set_temperature(t) ise.run(scattering_class, is_triplet_symmetry=self._interaction.get_is_symmetrize_fc3_q()) gamma[i] = ise.get_imag_self_energy() for i, bi in enumerate(self._band_indices): lw_on_path[p,:,i]=gamma[:, i] * 2 new_path = np.array(new_path) distances=map(lambda x: np.linalg.norm(np.dot(x-new_path[0], np.linalg.inv(self._primitive.get_cell().T))),new_path) distances=np.array(distances, dtype="double") return (new_path, distances, freqs_on_path, lw_on_path) def get_linewidth_at_grid_points(self, ise, sigmas, temperatures, grid_points, is_nu, scattering_class, filename): for gp in grid_points: ise.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Linewidth ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) for sigma in sigmas: ise.set_sigma(sigma) ise.run_interaction(scattering_class, self._read_amplitude) gamma = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') if is_nu: gamma_N=np.zeros_like(gamma) gamma_U=np.zeros_like(gamma) for i, t in enumerate(temperatures): ise.set_temperature(t) ise.run(scattering_class, is_triplet_symmetry=False) gamma[i] = ise.get_imag_self_energy() if is_nu: gamma_N[i]=ise.get_imag_self_energy_N() gamma_U[i]=ise.get_imag_self_energy_U() for i, bi in enumerate(self._band_indices): # pos = 0 # for j in range(i): # pos += len(self._band_indices[j]) if not is_nu: write_linewidth(gp, bi, temperatures, gamma[:, i], self._mesh, sigma=sigma, filename=filename) else: write_linewidth(gp, bi, temperatures, gamma[:, i], self._mesh, sigma=sigma, filename=filename, gamma_N=gamma_N[:, i], gamma_U=gamma_U[:, i]) def get_frequency_shift(self, grid_points, epsilon=0.1, temperatures=np.linspace(0,1000,endpoint=True, num=101), filename=None): fst = FrequencyShift(self._interaction) for gp in grid_points: fst.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Frequency shift -o- ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) fst.run_interaction() fst.set_epsilon(epsilon) delta = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') for i, t in enumerate(temperatures): fst.set_temperature(t) fst.run() delta[i] = fst.get_frequency_shift() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_frequency_shift(gp, bi, temperatures, delta[:, pos:(pos+len(bi))], self._mesh, epsilon=epsilon, filename=filename) def get_thermal_conductivity(self, sigmas=None, temperatures=None, grid_points=None, mesh_divisors=None, coarse_mesh_shifts=None, cutoff_lifetime=1e-4, # in second diff_kappa = 1e-5, # relative nu=None, no_kappa_stars=False, gv_delta_q=1e-4, # for group velocity write_gamma=False, read_gamma=False, kappa_write_step=None, filename=None): br = conductivity_RTA(self._interaction, symmetry=self._primitive_symmetry, sigmas=sigmas, asigma_step= self._asigma_step, temperatures=temperatures, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, grid_points=grid_points, cutoff_lifetime=cutoff_lifetime, diff_kappa= diff_kappa, nu=nu, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, log_level=self._log_level, write_tecplot = self._write_tecplot, kappa_write_step=kappa_write_step, is_thm=self._is_thm, filename=filename) if read_gamma: for sigma in sigmas: self.read_gamma_at_sigma(sigma) br.calculate_kappa(write_gamma=write_gamma) mode_kappa = br.get_kappa() gamma = br.get_gamma() gamma_N=br._gamma_N gamma_U=br._gamma_U if self._log_level: br.print_kappa() if grid_points is None: temperatures = br.get_temperatures() for i, sigma in enumerate(sigmas): kappa = mode_kappa[i] write_kappa_to_hdf5(gamma[i], temperatures, br.get_mesh_numbers(), frequency=br.get_frequencies(), group_velocity=br.get_group_velocities(), heat_capacity=br.get_mode_heat_capacities(), kappa=kappa, qpoint=br.get_qpoints(), weight=br.get_grid_weights(), mesh_divisors=br.get_mesh_divisors(), sigma=sigma, filename=filename, gnu=(gamma_N[i],gamma_U[i])) if self._write_tecplot: for j,temp in enumerate(temperatures): write_kappa_to_tecplot_BZ(np.where(gamma[i,:,j]>1e-8, gamma[i,:,j],0), temp, br.get_mesh_numbers(), bz_q_address=br._bz_grid_address / br.get_mesh_numbers().astype(float), tetrahedrdons=br._unique_vertices, bz_to_pp_mapping=br._bz_to_pp_map, rec_lattice=np.linalg.inv(br._primitive.get_cell()), spg_indices_mapping=br._irr_index_mapping, spg_rotation_mapping=br._rot_mappings, frequency=br.get_frequencies(), group_velocity=br.get_group_velocities(), heat_capacity=br.get_mode_heat_capacities()[:,j], kappa=kappa[:,j], weight=br.get_grid_weights(), sigma=sigma, filename="bz") self._kappa = mode_kappa self._gamma = gamma self._br=br def read_gamma_at_sigma(self, sigma): br = self._br gamma = [] try: properties = read_kappa_from_hdf5( br.get_mesh_numbers(), mesh_divisors=br.get_mesh_divisors(), sigma=sigma, filename=br._filename) if properties is None: raise ValueError tbr = br.get_temperatures() ts_pos0, ts_pos1 = np.where(properties['temperature'] == tbr.reshape(-1,1)) br.set_temperatures(tbr[ts_pos0]) gamma_at_sigma=properties['gamma'][:,ts_pos1] gamma.append(gamma_at_sigma) br.broadcast_collision_out(np.double(gamma)) except ValueError: properties = [] for point in br.get_grid_points(): property = read_kappa_from_hdf5( br.get_mesh_numbers(), mesh_divisors=br.get_mesh_divisors(), grid_point=point, sigma=sigma, filename=br._filename) properties.append(property) tbr = br.get_temperatures() ts_pos0,ts_pos1 = np.where(properties[0]['temperature'] == tbr.reshape(-1,1)) br.set_temperatures(tbr[ts_pos0]) gamma_at_sigma = np.array([p['gamma'][ts_pos1] for p in properties], dtype="double") gamma.append(gamma_at_sigma) br.broadcast_collision_out(np.double(gamma)) def get_kappa_ite(self, sigmas=[0.2], temperatures=None, grid_points=None, max_ite = None, no_kappa_stars=False, diff_kappa = 1e-5, # relative difference write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="ite"): bis=conductivity_ITE(self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points = grid_points, temperatures=temperatures, max_ite = max_ite, adaptive_sigma_step = self._asigma_step, no_kappa_stars=no_kappa_stars, diff_kappa= diff_kappa, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma = read_gamma, write_gamma = write_gamma, read_col = read_col, write_col=write_col, filename=filename) for s, sigma in enumerate(sigmas): write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename="smrt") try: for bi in bis: bi.set_kappa() print "After %d iteration(s), the thermal conductivities are recalculated to be (W/mK)"%bi._ite_step bi.print_kappa() except KeyboardInterrupt: print print "A keyboard Interruption is captured. The iterations are terminated!" print "The kappa is retrieved from the last iterations." bis._F = bis._F_prev print "Final thermal conductivity (W/mK)" for s, sigma in enumerate(sigmas): bis.set_equivalent_gamma_at_sigma(s) bis.set_kappa_at_sigma(s) write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename=bis._filename) self._gamma=bis._gamma self._kappa=bis._kappa bis.print_kappa() # bis.print_kappa_rta() def get_kappa_dinv(self, sigmas=[0.2], temperatures=None, grid_points=None, no_kappa_stars=False, write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="dinv"): bis=conductivity_DINV(self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points = grid_points, temperatures=temperatures, adaptive_sigma_step = self._asigma_step, no_kappa_stars=no_kappa_stars, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma = read_gamma, write_gamma = write_gamma, read_col = read_col, write_col=write_col, filename=filename) bis.run() bis.set_kappa() print "Final thermal conductivity (W/mK)" # for s, sigma in enumerate(sigmas): # bis.set_equivalent_gamma_at_sigma(s) # bis.set_kappa_at_sigma(s) # write_kappa_to_hdf5(bis._gamma[s], # bis._temperatures, # bis._mesh, # frequency=bis._frequencies, # group_velocity=bis._gv, # heat_capacity=bis.get_mode_heat_capacities(), # kappa=bis._kappa[s], # qpoint=bis._qpoints, # weight=bis._grid_weights, # sigma=sigma, # filename=bis._filename) self._gamma=bis._gamma self._kappa=bis._kappa bis.print_kappa() # bis.print_kappa_rta() def get_kappa_ite_cg(self, sigmas=[0.2], temperatures=None, grid_points=None, max_ite = None, no_kappa_stars=False, diff_kappa = 1e-5, # relative value write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="ite_cg"): bis=conductivity_ITE_CG(self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points = grid_points, temperatures=temperatures, max_ite = max_ite, adaptive_sigma_step = self._asigma_step, no_kappa_stars=no_kappa_stars, diff_kappa= diff_kappa, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma = read_gamma, write_gamma = write_gamma, read_col = read_col, write_col=write_col, is_thm=self._is_thm, filename=filename) for s, sigma in enumerate(sigmas): write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename="smrt") try: for bi in bis: bi.set_kappa() print "After %d iteration(s), the thermal conductivities are recalculated to be (W/mK)"%bi._ite_step bi.print_kappa() except KeyboardInterrupt: print print "A keyboard Interruption is captured. The iterations are terminated!" print "The kappa is retrieved from the last iterations." bis._F = bis._F_prev print "Final thermal conductivity (W/mK)" for s, sigma in enumerate(sigmas): bis.set_equivalent_gamma_at_sigma(s) bis.set_kappa_at_sigma(s) write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename=bis._filename) self._gamma=bis._gamma self._kappa=bis._kappa bis.print_kappa()
class Phono3py: def __init__( self, interaction, mass_variances=None, length=None, adaptive_sigma_step=0, frequency_factor_to_THz=None, is_nosym=False, symprec=1e-5, log_level=0, is_thm=False, # is tetrahedron method write_tecplot=False): self._interaction = interaction self._primitive = interaction.get_primitive() self._supercell = interaction.get_supercell() self._mesh = interaction.get_mesh_numbers() self._band_indices = interaction.get_band_indices() self._frequency_factor_to_THz = frequency_factor_to_THz self._is_nosym = is_nosym self._is_symmetry = not is_nosym self._symprec = symprec self._log_level = log_level self._asigma_step = adaptive_sigma_step self._step = 0 self._kappa = None self._gamma = None self._br = None self._is_thm = is_thm self._write_tecplot = write_tecplot self._mass_variances = mass_variances self._length = length self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry 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 set_dynamical_matrix(self, fc2, supercell, primitive, nac_params=None, nac_q_direction=None, frequency_scale_factor=None): self._interaction.set_dynamical_matrix( fc2, supercell, primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) def get_imag_self_energy(self, grid_points, frequency_step=1.0, sigmas=[None], temperatures=[0.0], filename=None): ise = ImagSelfEnergy(self._interaction, is_thm=self._is_thm) for gp in grid_points: ise.set_grid_point(gp) ise.run_interaction() for sigma in sigmas: ise.set_sigma(sigma) for t in temperatures: ise.set_temperature(t) max_freq = ( np.amax(self._interaction.get_phonons()[0]) * 2 + sigma * 4) fpoints = np.arange(0, max_freq + frequency_step / 2, frequency_step) ise.set_fpoints(fpoints) ise.run() gamma = ise.get_imag_self_energy() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_damping_functions( gp, bi, self._mesh, fpoints, gamma[:, pos:(pos + len(bi))].sum(axis=1) / len(bi), sigma=sigma, temperature=t, filename=filename) def get_matrix_contribution(self, grid_points, frequency_step=1.0, sigmas=[0.1], is_adaptive_sigma=False, filename=None): mc = Collision(self._interaction, sigmas=sigmas, is_adaptive_sigma=is_adaptive_sigma, frequencies=self._frequencies, degeneracy=self._degeneracy, write=write_scr, read=read_scr, cutoff_frequency=self._cutfr, cutoff_lifetime=self._cutlt) is_sum = False if grid_points == None: is_sum = True if self._is_nosym: # All grid points grid_points = np.arange(np.product(self._mesh)) else: # Automatic sampling (grid_points, grid_weights, grid_address) = get_ir_grid_points(self._mesh, self._primitive) matrix_contributions = [] for gp in grid_points: print "# grid %d" % gp mc.set_grid_point(gp) mc.run_interaction() matrix_contributions_temp = [] for sigma in sigmas: mc.set_sigma(sigma) max_freq = (np.amax(self._interaction.get_phonons()[0]) * 2 + sigma * 4) fpoints = np.arange(0, max_freq + frequency_step / 2, frequency_step) mc.set_fpoints(fpoints) mc.run() matrix_contribution = mc.get_imag_self_energy() matrix_contributions_temp.append(matrix_contribution) if not is_sum: write_matrix_contribution(gp, self._mesh, fpoints, matrix_contribution, sigma=sigma, filename=filename) matrix_contributions.append(matrix_contributions_temp) matrix_contributions = np.array(matrix_contributions) if is_sum: for i, sigma in enumerate(sigmas): write_matrix_contribution(None, self._mesh, fpoints, np.sum(matrix_contributions[:, i], axis=0), sigma=sigma, filename=filename) def get_decay_channels(self, grid_points, sets_of_band_indices, sigmas=[0.2], nu=None, scattering_class=None, read_amplitude=False, temperature=None, filename=None): self._interaction.set_is_disperse(True) if grid_points == None: print "Grid points are not specified." return False if sets_of_band_indices == None: print "Band indices are not specified." return False self._read_amplitude = read_amplitude decay = DecayChannel(self._interaction, nu=nu, scattering_class=scattering_class) for gp in grid_points: decay.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Decay channel ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) # decay.run_interaction() for sigma in sigmas: decay.set_sigma(sigma) decay.run_interaction() for i, t in enumerate(temperature): decay.set_temperature(t) decay.get_decay_channels(filename=filename) def get_linewidth(self, grid_points, sigmas=[0.1], temperatures=None, read_amplitude=False, nu=None, scattering_class=None, band_paths=None, filename=None): self._interaction.set_is_disperse(True) ise = ImagSelfEnergy(self._interaction, nu=nu) if temperatures is None: temperatures = np.arange(0, 1000.0 + 10.0 / 2.0, 10.0) self._temperatures = temperatures self._read_amplitude = read_amplitude if grid_points is not None: self.get_linewidth_at_grid_points(ise, sigmas, temperatures, grid_points, nu, scattering_class, filename) elif band_paths is not None: self.get_linewidth_at_paths(ise, sigmas, temperatures, band_paths, nu, scattering_class, filename) def get_linewidth_at_paths(self, ise, sigmas, temperatures, band_paths, is_nu, scattering_class=None, filename=None): if self._is_nosym: grid_address, grid_mapping = get_grid_address( [x + (x % 2 == 0) for x in self._mesh], is_return_map=True) else: grids, weights, grid_address, grid_mapping = get_ir_grid_points( [x + (x % 2 == 0) for x in self._mesh], self._primitive, is_return_map=True) qpoints_address = grid_address / np.array(self._mesh, dtype=float) print "Paths in reciprocal reduced coordinates:" for sigma in sigmas: self._sigma = sigma lws_paths = [] distances = [] frequencies = [] new_paths = [] for path in band_paths: print "[%5.2f %5.2f %5.2f] --> [%5.2f %5.2f %5.2f]" % ( tuple(path[0]) + tuple(path[-1])) (new_path, dists, freqs, lws) = self.path(path, qpoints_address, grid_mapping, ise, temperatures, scattering_class) new_paths.append(new_path) distances.append(dists) frequencies.append(freqs) lws_paths.append(lws) self._distances = distances self._freqs_on_path = frequencies self._lws_on_path = lws_paths write_linewidth_band_csv( self._mesh, new_paths, distances=self._distances, lws=self._lws_on_path, band_indices=self._band_indices, temperatures=temperatures, frequencies=self._freqs_on_path, scattering_class=scattering_class, nu=is_nu, filename=(("-s%s" % sigma) if sigma is not None else "")) self.plot_lw() def plot_lw(self, symbols=None): import matplotlib.pyplot as plt if symbols: from matplotlib import rc rc('text', usetex=True) distances = np.array(sum(np.array(self._distances).tolist(), [])) special_point = [0.0] + [d[-1] for d in self._distances] lws = np.concatenate(tuple(self._lws_on_path), axis=0) for t in np.arange(lws.shape[1]): for b in np.arange(lws.shape[2]): plt.figure() plt.plot(distances, lws[:, t, b]) plt.ylabel('Linewidth') plt.xlabel('Wave vector') if symbols and len(symbols) == len(special_point): plt.xticks(special_point, symbols) else: plt.xticks(special_point, [''] * len(special_point)) plt.xlim(0, special_point[-1]) plt.axhline(y=0, linestyle=':', linewidth=0.5, color='b') if len(lws.shape) == 4: plt.legend(["Total", "Normal process", "Umklapp process"]) plt.savefig("linewidth-t%d-b%d.pdf" % (self._temperatures[t], self._band_indices[b] + 1)) plt.show() def path(self, path, qpoints_address, grids_mapping, ise, temperatures, scattering_class=None): lw_on_path = np.zeros( (len(path), len(temperatures), len(self._band_indices)), dtype="double") freqs_on_path = np.zeros((len(path), len(self._band_indices)), dtype="double") new_path = [] for p, qpoint in enumerate(path): # dist_vector=np.abs(qpoints_address-qpoint) distance = np.sqrt(np.sum((qpoints_address - qpoint)**2, axis=-1)) grid = np.argmin(distance) ise.set_grid_point(grids_mapping[grid]) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Linewidth ------(%d/%d)" % (p, len(path)) print "calculated at qpoint", qpoint print "and represented by grid: %d (%.7f %.7f %.7f) due to the finite mesh" % ( grid, qpoints_address[grid][0], qpoints_address[grid][1], qpoints_address[grid][2]) print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) new_path.append(qpoints_address[grid]) ise.set_sigma(self._sigma) ise.run_interaction(scattering_class, self._read_amplitude) freqs_on_path[p] = self._interaction._frequencies[ grids_mapping[grid]][self._band_indices] gamma = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') for i, t in enumerate(temperatures): ise.set_temperature(t) ise.run(scattering_class, is_triplet_symmetry=self._interaction. get_is_symmetrize_fc3_q()) gamma[i] = ise.get_imag_self_energy() for i, bi in enumerate(self._band_indices): lw_on_path[p, :, i] = gamma[:, i] * 2 new_path = np.array(new_path) distances = map( lambda x: np.linalg.norm( np.dot(x - new_path[0], np.linalg.inv(self._primitive.get_cell().T))), new_path) distances = np.array(distances, dtype="double") return (new_path, distances, freqs_on_path, lw_on_path) def get_linewidth_at_grid_points(self, ise, sigmas, temperatures, grid_points, is_nu, scattering_class, filename): for gp in grid_points: ise.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Linewidth ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) for sigma in sigmas: ise.set_sigma(sigma) ise.run_interaction(scattering_class, self._read_amplitude) gamma = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') if is_nu: gamma_N = np.zeros_like(gamma) gamma_U = np.zeros_like(gamma) for i, t in enumerate(temperatures): ise.set_temperature(t) ise.run(scattering_class, is_triplet_symmetry=False) gamma[i] = ise.get_imag_self_energy() if is_nu: gamma_N[i] = ise.get_imag_self_energy_N() gamma_U[i] = ise.get_imag_self_energy_U() for i, bi in enumerate(self._band_indices): # pos = 0 # for j in range(i): # pos += len(self._band_indices[j]) if not is_nu: write_linewidth(gp, bi, temperatures, gamma[:, i], self._mesh, sigma=sigma, filename=filename) else: write_linewidth(gp, bi, temperatures, gamma[:, i], self._mesh, sigma=sigma, filename=filename, gamma_N=gamma_N[:, i], gamma_U=gamma_U[:, i]) def get_frequency_shift(self, grid_points, epsilon=0.1, temperatures=np.linspace(0, 1000, endpoint=True, num=101), filename=None): fst = FrequencyShift(self._interaction) for gp in grid_points: fst.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Frequency shift -o- ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) fst.run_interaction() fst.set_epsilon(epsilon) delta = np.zeros((len(temperatures), len(self._band_indices)), dtype='double') for i, t in enumerate(temperatures): fst.set_temperature(t) fst.run() delta[i] = fst.get_frequency_shift() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_frequency_shift(gp, bi, temperatures, delta[:, pos:(pos + len(bi))], self._mesh, epsilon=epsilon, filename=filename) def get_thermal_conductivity( self, sigmas=None, temperatures=None, grid_points=None, mesh_divisors=None, coarse_mesh_shifts=None, cutoff_lifetime=1e-4, # in second diff_kappa=1e-5, # relative nu=None, no_kappa_stars=False, gv_delta_q=1e-4, # for group velocity write_gamma=False, read_gamma=False, kappa_write_step=None, filename=None): br = conductivity_RTA(self._interaction, symmetry=self._primitive_symmetry, sigmas=sigmas, asigma_step=self._asigma_step, temperatures=temperatures, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, grid_points=grid_points, cutoff_lifetime=cutoff_lifetime, diff_kappa=diff_kappa, nu=nu, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, log_level=self._log_level, write_tecplot=self._write_tecplot, kappa_write_step=kappa_write_step, is_thm=self._is_thm, filename=filename) if read_gamma: for sigma in sigmas: self.read_gamma_at_sigma(sigma) br.calculate_kappa(write_gamma=write_gamma) mode_kappa = br.get_kappa() gamma = br.get_gamma() gamma_N = br._gamma_N gamma_U = br._gamma_U if self._log_level: br.print_kappa() if grid_points is None: temperatures = br.get_temperatures() for i, sigma in enumerate(sigmas): kappa = mode_kappa[i] write_kappa_to_hdf5( gamma[i], temperatures, br.get_mesh_numbers(), frequency=br.get_frequencies(), group_velocity=br.get_group_velocities(), heat_capacity=br.get_mode_heat_capacities(), kappa=kappa, qpoint=br.get_qpoints(), weight=br.get_grid_weights(), mesh_divisors=br.get_mesh_divisors(), sigma=sigma, filename=filename, gnu=(gamma_N[i], gamma_U[i])) if self._write_tecplot: for j, temp in enumerate(temperatures): write_kappa_to_tecplot_BZ( np.where(gamma[i, :, j] > 1e-8, gamma[i, :, j], 0), temp, br.get_mesh_numbers(), bz_q_address=br._bz_grid_address / br.get_mesh_numbers().astype(float), tetrahedrdons=br._unique_vertices, bz_to_pp_mapping=br._bz_to_pp_map, rec_lattice=np.linalg.inv( br._primitive.get_cell()), spg_indices_mapping=br._irr_index_mapping, spg_rotation_mapping=br._rot_mappings, frequency=br.get_frequencies(), group_velocity=br.get_group_velocities(), heat_capacity=br.get_mode_heat_capacities()[:, j], kappa=kappa[:, j], weight=br.get_grid_weights(), sigma=sigma, filename="bz") self._kappa = mode_kappa self._gamma = gamma self._br = br def read_gamma_at_sigma(self, sigma): br = self._br gamma = [] try: properties = read_kappa_from_hdf5( br.get_mesh_numbers(), mesh_divisors=br.get_mesh_divisors(), sigma=sigma, filename=br._filename) if properties is None: raise ValueError tbr = br.get_temperatures() ts_pos0, ts_pos1 = np.where( properties['temperature'] == tbr.reshape(-1, 1)) br.set_temperatures(tbr[ts_pos0]) gamma_at_sigma = properties['gamma'][:, ts_pos1] gamma.append(gamma_at_sigma) br.broadcast_collision_out(np.double(gamma)) except ValueError: properties = [] for point in br.get_grid_points(): property = read_kappa_from_hdf5( br.get_mesh_numbers(), mesh_divisors=br.get_mesh_divisors(), grid_point=point, sigma=sigma, filename=br._filename) properties.append(property) tbr = br.get_temperatures() ts_pos0, ts_pos1 = np.where( properties[0]['temperature'] == tbr.reshape(-1, 1)) br.set_temperatures(tbr[ts_pos0]) gamma_at_sigma = np.array( [p['gamma'][ts_pos1] for p in properties], dtype="double") gamma.append(gamma_at_sigma) br.broadcast_collision_out(np.double(gamma)) def get_kappa_ite( self, sigmas=[0.2], temperatures=None, grid_points=None, max_ite=None, no_kappa_stars=False, diff_kappa=1e-5, # relative difference write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="ite"): bis = conductivity_ITE( self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points=grid_points, temperatures=temperatures, max_ite=max_ite, adaptive_sigma_step=self._asigma_step, no_kappa_stars=no_kappa_stars, diff_kappa=diff_kappa, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma=read_gamma, write_gamma=write_gamma, read_col=read_col, write_col=write_col, filename=filename) for s, sigma in enumerate(sigmas): write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename="smrt") try: for bi in bis: bi.set_kappa() print "After %d iteration(s), the thermal conductivities are recalculated to be (W/mK)" % bi._ite_step bi.print_kappa() except KeyboardInterrupt: print print "A keyboard Interruption is captured. The iterations are terminated!" print "The kappa is retrieved from the last iterations." bis._F = bis._F_prev print "Final thermal conductivity (W/mK)" for s, sigma in enumerate(sigmas): bis.set_equivalent_gamma_at_sigma(s) bis.set_kappa_at_sigma(s) write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename=bis._filename) self._gamma = bis._gamma self._kappa = bis._kappa bis.print_kappa() # bis.print_kappa_rta() def get_kappa_dinv(self, sigmas=[0.2], temperatures=None, grid_points=None, no_kappa_stars=False, write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="dinv"): bis = conductivity_DINV( self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points=grid_points, temperatures=temperatures, adaptive_sigma_step=self._asigma_step, no_kappa_stars=no_kappa_stars, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma=read_gamma, write_gamma=write_gamma, read_col=read_col, write_col=write_col, filename=filename) bis.run() bis.set_kappa() print "Final thermal conductivity (W/mK)" # for s, sigma in enumerate(sigmas): # bis.set_equivalent_gamma_at_sigma(s) # bis.set_kappa_at_sigma(s) # write_kappa_to_hdf5(bis._gamma[s], # bis._temperatures, # bis._mesh, # frequency=bis._frequencies, # group_velocity=bis._gv, # heat_capacity=bis.get_mode_heat_capacities(), # kappa=bis._kappa[s], # qpoint=bis._qpoints, # weight=bis._grid_weights, # sigma=sigma, # filename=bis._filename) self._gamma = bis._gamma self._kappa = bis._kappa bis.print_kappa() # bis.print_kappa_rta() def get_kappa_ite_cg( self, sigmas=[0.2], temperatures=None, grid_points=None, max_ite=None, no_kappa_stars=False, diff_kappa=1e-5, # relative value write_gamma=False, read_gamma=False, read_col=False, write_col=False, filename="ite_cg"): bis = conductivity_ITE_CG( self._interaction, #Iterative Boltzmann solutions symmetry=self._primitive_symmetry, sigmas=sigmas, grid_points=grid_points, temperatures=temperatures, max_ite=max_ite, adaptive_sigma_step=self._asigma_step, no_kappa_stars=no_kappa_stars, diff_kappa=diff_kappa, mass_variances=self._mass_variances, length=self._length, log_level=self._log_level, read_gamma=read_gamma, write_gamma=write_gamma, read_col=read_col, write_col=write_col, is_thm=self._is_thm, filename=filename) for s, sigma in enumerate(sigmas): write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename="smrt") try: for bi in bis: bi.set_kappa() print "After %d iteration(s), the thermal conductivities are recalculated to be (W/mK)" % bi._ite_step bi.print_kappa() except KeyboardInterrupt: print print "A keyboard Interruption is captured. The iterations are terminated!" print "The kappa is retrieved from the last iterations." bis._F = bis._F_prev print "Final thermal conductivity (W/mK)" for s, sigma in enumerate(sigmas): bis.set_equivalent_gamma_at_sigma(s) bis.set_kappa_at_sigma(s) write_kappa_to_hdf5(bis._gamma[s], bis._temperatures, bis._mesh, frequency=bis._frequencies, group_velocity=bis._gv, heat_capacity=bis.get_mode_heat_capacities(), kappa=bis._kappa[s], qpoint=bis._qpoints, weight=bis._grid_weights, sigma=sigma, filename=bis._filename) self._gamma = bis._gamma self._kappa = bis._kappa bis.print_kappa()
class Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3_q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3_q = symmetrize_fc3_q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None # Linewidth (Imaginary part of self energy x 2) at temperatures self._linewidth = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._mesh = np.array(mesh, dtype='intc') self.set_band_indices(band_indices) def set_band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack(self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None): self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, frequency_scale_factor=frequency_scale_factor) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def write_phonons(self, filename=None): if self._interaction is not None: grid_address = self._interaction.get_grid_address() grid_points = np.arange(len(grid_address), dtype='intc') self._interaction.set_phonons(grid_points) freqs, eigvecs, _ = self._interaction.get_phonons() hdf5_filename = write_phonon_to_hdf5(freqs, eigvecs, grid_address, self._mesh, filename=filename) return hdf5_filename else: return False def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, is_translational_symmetry=False, is_permutation_symmetry=False, translational_symmetry_type=None, use_alm=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc2 as get_fc2_alm self._fc2 = get_fc2_alm(self._phonon_supercell, forces_fc2, disp_dataset, self._phonon_supercell_symmetry) else: for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(self._fc2) if is_translational_symmetry: tsym_type = 1 else: tsym_type = 0 if translational_symmetry_type: tsym_type = translational_symmetry_type if tsym_type: set_translational_invariance( self._fc2, translational_symmetry_type=tsym_type) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero is_translational_symmetry=False, is_permutation_symmetry=False, is_permutation_symmetry_fc2=False, translational_symmetry_type=None, use_alm=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc3 as get_fc3_alm fc2, fc3 = get_fc3_alm(self._supercell, forces_fc3, disp_dataset, self._symmetry) else: fc2, fc3 = self._get_fc3(forces_fc3, disp_dataset, cutoff_distance, is_translational_symmetry, is_permutation_symmetry, is_permutation_symmetry_fc2, translational_symmetry_type) # Set fc2 and fc3 self._fc3 = fc3 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero(_fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self, translational_symmetry_type=1): if self._fc2 is not None: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) if self._fc3 is not None: set_translational_invariance_fc3( self._fc3, translational_symmetry_type=translational_symmetry_type) def get_version(self): return __version__ def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 def get_nac_params(self): return self._nac_params def get_primitive(self): return self._primitive def get_unitcell(self): return self._unitcell def get_supercell(self): return self._supercell def get_phonon_supercell(self): return self._phonon_supercell def get_phonon_primitive(self): return self._phonon_primitive def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def get_supercell_matrix(self): return self._supercell_matrix def get_primitive_matrix(self): return self._primitive_matrix def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset def get_displacement_dataset(self): return self._displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_linewidth(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), write_gamma_detail=False): if self._interaction is None: self.set_phph_interaction() self._grid_points = grid_points self._temperatures = temperatures self._linewidth = get_linewidth(self._interaction, grid_points, self._sigmas, temperatures=temperatures, write_detail=write_gamma_detail, log_level=self._log_level) def write_linewidth(self, filename=None): write_linewidth(self._linewidth, self._band_indices, self._mesh, self._grid_points, self._sigmas, self._temperatures, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_gamma_detail=write_gamma_detail, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), output_filename=None): if self._interaction is None: self.set_phph_interaction() if epsilons is None: epsilons = [0.1] self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, self._sigmas, temperatures, output_filename=output_filename, log_level=self._log_level) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if self._primitive is not None: if (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any(): print("********************* Warning *********************") print(" Primitive cells for fc2 and fc3 can be different.") print("********************* Warning *********************") def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[ [p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map()]] self._phonon_supercell.set_masses(s_masses) def _get_fc3(self, forces_fc3, disp_dataset, cutoff_distance, is_translational_symmetry, is_permutation_symmetry, is_permutation_symmetry_fc2, translational_symmetry_type): for forces, disp1 in zip(forces_fc3, disp_dataset['first_atoms']): disp1['forces'] = forces fc2 = get_fc2(self._supercell, self._symmetry, disp_dataset) if is_permutation_symmetry_fc2: set_permutation_symmetry(fc2) if is_translational_symmetry: tsym_type = 1 else: tsym_type = 0 if translational_symmetry_type: tsym_type = translational_symmetry_type if tsym_type: set_translational_invariance( fc2, translational_symmetry_type=tsym_type) count = len(disp_dataset['first_atoms']) for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 fc3 = get_fc3( self._supercell, disp_dataset, fc2, self._symmetry, translational_symmetry_type=tsym_type, is_permutation_symmetry=is_permutation_symmetry, verbose=self._log_level) # Set fc3 elements zero beyond cutoff_distance if cutoff_distance: if self._log_level: print("Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance) self.cutoff_fc3_by_zero(cutoff_distance, fc3=fc3) return fc2, fc3
class JointDos(object): def __init__(self, mesh, primitive, supercell, fc2, nac_params=None, nac_q_direction=None, sigma=None, cutoff_frequency=None, frequency_step=None, num_frequency_points=None, temperatures=None, frequency_factor_to_THz=VaspToTHz, frequency_scale_factor=1.0, is_mesh_symmetry=True, symprec=1e-5, filename=None, log_level=False, lapack_zheev_uplo='L'): self._grid_point = None self._mesh = np.array(mesh, dtype='intc') self._primitive = primitive self._supercell = supercell self._fc2 = fc2 self._nac_params = nac_params self._nac_q_direction = None self.set_nac_q_direction(nac_q_direction) self._sigma = None self.set_sigma(sigma) if cutoff_frequency is None: self._cutoff_frequency = 0 else: self._cutoff_frequency = cutoff_frequency self._frequency_step = frequency_step self._num_frequency_points = num_frequency_points self._temperatures = temperatures self._frequency_factor_to_THz = frequency_factor_to_THz self._frequency_scale_factor = frequency_scale_factor self._is_mesh_symmetry = is_mesh_symmetry self._symprec = symprec self._filename = filename self._log_level = log_level self._lapack_zheev_uplo = lapack_zheev_uplo self._num_band = self._primitive.get_number_of_atoms() * 3 self._reciprocal_lattice = np.linalg.inv(self._primitive.get_cell()) self._init_dynamical_matrix() self._symmetry = Symmetry(primitive, symprec) self._tetrahedron_method = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._joint_dos = None self._frequency_points = None def run(self): try: import phono3py._phono3py as phono3c self._run_c() except ImportError: print("Joint density of states in python is not implemented.") return None, None @property def dynamical_matrix(self): return self._dm @property def joint_dos(self): return self._joint_dos def get_joint_dos(self): warnings.warn("Use attribute, joint_dos", DeprecationWarning) return self.joint_dos @property def frequency_points(self): return self._frequency_points def get_frequency_points(self): warnings.warn("Use attribute, frequency_points", DeprecationWarning) return self.frequency_points def get_phonons(self): return self._frequencies, self._eigenvectors, self._phonon_done @property def primitive(self): return self._primitive def get_primitive(self): warnings.warn("Use attribute, primitive", DeprecationWarning) return self.primitive @property def mesh_numbers(self): return self._mesh def get_mesh_numbers(self): warnings.warn("Use attribute, mesh_numbers", DeprecationWarning) return self.mesh def set_nac_q_direction(self, nac_q_direction=None): if nac_q_direction is not None: self._nac_q_direction = np.array(nac_q_direction, dtype='double') def set_sigma(self, sigma): if sigma is None: self._sigma = None else: self._sigma = float(sigma) def set_grid_point(self, grid_point): self._grid_point = grid_point self._set_triplets() num_grid = np.prod(len(self._grid_address)) num_band = self._num_band if self._phonon_done is None: self._phonon_done = np.zeros(num_grid, dtype='byte') self._frequencies = np.zeros((num_grid, num_band), dtype='double') itemsize = self._frequencies.itemsize self._eigenvectors = np.zeros((num_grid, num_band, num_band), dtype=("c%d" % (itemsize * 2))) self._joint_dos = None self._frequency_points = None self.run_phonon_solver(np.array([grid_point], dtype='uintp')) def get_triplets_at_q(self): return self._triplets_at_q, self._weights_at_q @property def grid_address(self): return self._grid_address def get_grid_address(self): warnings.warn("Use attribute, grid_address", DeprecationWarning) return self.grid_address @property def bz_map(self): return self._bz_map def get_bz_map(self): warnings.warn("Use attribute, bz_map", DeprecationWarning) return self._bz_map def _run_c(self, lang='C'): if self._sigma is None: if lang == 'C': self._run_c_with_g() else: if self._temperatures is not None: print("JDOS with phonon occupation numbers doesn't work " "in this option.") self._run_py_tetrahedron_method() else: self._run_c_with_g() def _run_c_with_g(self): self.run_phonon_solver(self._triplets_at_q.ravel()) max_phonon_freq = np.max(self._frequencies) self._frequency_points = get_frequency_points( max_phonon_freq=max_phonon_freq, sigmas=[ self._sigma, ], frequency_points=None, frequency_step=self._frequency_step, num_frequency_points=self._num_frequency_points) num_freq_points = len(self._frequency_points) num_mesh = np.prod(self._mesh) if self._temperatures is None: jdos = np.zeros((num_freq_points, 2), dtype='double') else: num_temps = len(self._temperatures) jdos = np.zeros((num_temps, num_freq_points, 2), dtype='double') occ_phonons = [] for t in self._temperatures: freqs = self._frequencies[self._triplets_at_q[:, 1:]] occ_phonons.append( np.where(freqs > self._cutoff_frequency, bose_einstein(freqs, t), 0)) for i, freq_point in enumerate(self._frequency_points): g, _ = get_triplets_integration_weights( self, np.array([freq_point], dtype='double'), self._sigma, is_collision_matrix=True, neighboring_phonons=(i == 0)) if self._temperatures is None: jdos[i, 1] = np.sum( np.tensordot(g[0, :, 0], self._weights_at_q, axes=(0, 0))) gx = g[2] - g[0] jdos[i, 0] = np.sum( np.tensordot(gx[:, 0], self._weights_at_q, axes=(0, 0))) else: for j, n in enumerate(occ_phonons): for k, l in list(np.ndindex(g.shape[3:])): jdos[j, i, 1] += np.dot( (n[:, 0, k] + n[:, 1, l] + 1) * g[0, :, 0, k, l], self._weights_at_q) jdos[j, i, 0] += np.dot( (n[:, 0, k] - n[:, 1, l]) * g[1, :, 0, k, l], self._weights_at_q) self._joint_dos = jdos / num_mesh def _run_py_tetrahedron_method(self): thm = TetrahedronMethod(self._reciprocal_lattice, mesh=self._mesh) self._vertices = get_tetrahedra_vertices(thm.get_tetrahedra(), self._mesh, self._triplets_at_q, self._grid_address, self._bz_map) self.run_phonon_solver(self._vertices.ravel()) f_max = np.max(self._frequencies) * 2 f_max *= 1.005 f_min = 0 self._set_uniform_frequency_points(f_min, f_max) num_freq_points = len(self._frequency_points) jdos = np.zeros((num_freq_points, 2), dtype='double') for vertices, w in zip(self._vertices, self._weights_at_q): for i, j in list(np.ndindex(self._num_band, self._num_band)): f1 = self._frequencies[vertices[0], i] f2 = self._frequencies[vertices[1], j] thm.set_tetrahedra_omegas(f1 + f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 1] += iw * w thm.set_tetrahedra_omegas(f1 - f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 0] += iw * w thm.set_tetrahedra_omegas(-f1 + f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 0] += iw * w self._joint_dos = jdos / np.prod(self._mesh) def _init_dynamical_matrix(self): self._dm = get_dynamical_matrix( self._fc2, self._supercell, self._primitive, nac_params=self._nac_params, frequency_scale_factor=self._frequency_scale_factor, symprec=self._symprec) def _set_triplets(self): if not self._is_mesh_symmetry: if self._log_level: print("Triplets at q without considering symmetry") sys.stdout.flush() (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map, map_triplets, map_q) = get_nosym_triplets_at_q(self._grid_point, self._mesh, self._reciprocal_lattice, with_bz_map=True) else: (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map, map_triplets, map_q) = get_triplets_at_q( self._grid_point, self._mesh, self._symmetry.get_pointgroup_operations(), self._reciprocal_lattice) def run_phonon_solver(self, grid_points): run_phonon_solver_c(self._dm, self._frequencies, self._eigenvectors, self._phonon_done, grid_points, self._grid_address, self._mesh, self._frequency_factor_to_THz, self._nac_q_direction, self._lapack_zheev_uplo) def set_frequency_points(self, frequency_points): self._frequency_points = np.array(frequency_points, dtype='double')
class JointDos: def __init__(self, mesh, primitive, supercell, fc2, nac_params=None, nac_q_direction=None, sigma=None, cutoff_frequency=None, frequency_step=None, num_frequency_points=None, temperatures=None, frequency_factor_to_THz=VaspToTHz, frequency_scale_factor=1.0, is_nosym=False, symprec=1e-5, filename=None, log_level=False, lapack_zheev_uplo='L'): self._grid_point = None self._mesh = np.array(mesh, dtype='intc') self._primitive = primitive self._supercell = supercell self._fc2 = fc2 self._nac_params = nac_params self._nac_q_direction = None self.set_nac_q_direction(nac_q_direction) self._sigma = None self.set_sigma(sigma) if cutoff_frequency is None: self._cutoff_frequency = 0 else: self._cutoff_frequency = cutoff_frequency self._frequency_step = frequency_step self._num_frequency_points = num_frequency_points self._temperatures = temperatures self._frequency_factor_to_THz = frequency_factor_to_THz self._frequency_scale_factor = frequency_scale_factor self._is_nosym = is_nosym self._symprec = symprec self._filename = filename self._log_level = log_level self._lapack_zheev_uplo = lapack_zheev_uplo self._num_band = self._primitive.get_number_of_atoms() * 3 self._reciprocal_lattice = np.linalg.inv(self._primitive.get_cell()) self._set_dynamical_matrix() self._symmetry = Symmetry(primitive, symprec) self._tetrahedron_method = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._joint_dos = None self._frequency_points = None def run(self): try: import anharmonic._phono3py as phono3c self._run_c() except ImportError: print "Joint density of states in python is not implemented." return None, None def get_joint_dos(self): return self._joint_dos def get_frequency_points(self): return self._frequency_points def get_phonons(self): return self._frequencies, self._eigenvectors, self._phonon_done def get_primitive(self): return self._primitive def get_mesh_numbers(self): return self._mesh def set_nac_q_direction(self, nac_q_direction=None): if nac_q_direction is not None: self._nac_q_direction = np.array(nac_q_direction, dtype='double') def set_sigma(self, sigma): if sigma is None: self._sigma = None else: self._sigma = float(sigma) def set_grid_point(self, grid_point): self._grid_point = grid_point self._set_triplets() num_grid = np.prod(len(self._grid_address)) num_band = self._num_band if self._phonon_done is None: self._phonon_done = np.zeros(num_grid, dtype='byte') self._frequencies = np.zeros((num_grid, num_band), dtype='double') self._eigenvectors = np.zeros((num_grid, num_band, num_band), dtype='complex128') self._joint_dos = None self._frequency_points = None self.set_phonon(np.array([grid_point], dtype='intc')) def get_triplets_at_q(self): return self._triplets_at_q, self._weights_at_q def get_grid_address(self): return self._grid_address def get_bz_map(self): return self._bz_map def _run_c(self, lang='C'): if self._sigma is None: if lang == 'C': self._run_c_with_g() else: if self._temperatures is not None: print "JDOS with phonon occupation numbers doesn't work", print "in this option." self._run_py_tetrahedron_method() else: self._run_c_with_g() # self._run_smearing_method() is an older and direct implementation. # This requies less memory space. self._run_c_with_g can be used # for smearing method and can share same code with tetrahedron # method. Therefore maintainance cost of code can be reduced by # without using self._run_smearing_method(). def _run_c_with_g(self): self.set_phonon(self._triplets_at_q.ravel()) if self._sigma is None: f_max = np.max(self._frequencies) * 2 else: f_max = np.max(self._frequencies) * 2 + self._sigma * 4 f_max *= 1.005 f_min = 0 self._set_frequency_points(f_min, f_max) num_freq_points = len(self._frequency_points) num_mesh = np.prod(self._mesh) if self._temperatures is None: jdos = np.zeros((num_freq_points, 2), dtype='double') else: num_temps = len(self._temperatures) jdos = np.zeros((num_temps, num_freq_points, 2), dtype='double') occ_phonons = [] for t in self._temperatures: freqs = self._frequencies[self._triplets_at_q[:, 1:]] occ_phonons.append(np.where(freqs > self._cutoff_frequency, occupation(freqs, t), 0)) for i, freq_point in enumerate(self._frequency_points): g = get_triplets_integration_weights( self, np.array([freq_point], dtype='double'), self._sigma, is_collision_matrix=True, neighboring_phonons=(i == 0)) if self._temperatures is None: jdos[i, 0] = np.sum( np.tensordot(g[0, :, 0], self._weights_at_q, axes=(0, 0))) gx = g[2] - g[0] jdos[i, 1] = np.sum( np.tensordot(gx[:, 0], self._weights_at_q, axes=(0, 0))) else: for j, n in enumerate(occ_phonons): for k, l in list(np.ndindex(g.shape[3:])): jdos[j, i, 0] += np.dot( (n[:, 0, k] + n[:, 1, l] + 1) * g[0, :, 0, k, l], self._weights_at_q) jdos[j, i, 1] += np.dot((n[:, 0, k] - n[:, 1, l]) * g[1, :, 0, k, l], self._weights_at_q) self._joint_dos = jdos / num_mesh def _run_py_tetrahedron_method(self): thm = TetrahedronMethod(self._reciprocal_lattice, mesh=self._mesh) self._vertices = get_tetrahedra_vertices( thm.get_tetrahedra(), self._mesh, self._triplets_at_q, self._grid_address, self._bz_map) self.set_phonon(self._vertices.ravel()) f_max = np.max(self._frequencies) * 2 f_max *= 1.005 f_min = 0 self._set_frequency_points(f_min, f_max) num_freq_points = len(self._frequency_points) jdos = np.zeros((num_freq_points, 2), dtype='double') for vertices, w in zip(self._vertices, self._weights_at_q): for i, j in list(np.ndindex(self._num_band, self._num_band)): f1 = self._frequencies[vertices[0], i] f2 = self._frequencies[vertices[1], j] thm.set_tetrahedra_omegas(f1 + f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 0] += iw * w thm.set_tetrahedra_omegas(f1 - f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 1] += iw * w thm.set_tetrahedra_omegas(-f1 + f2) thm.run(self._frequency_points) iw = thm.get_integration_weight() jdos[:, 1] += iw * w self._joint_dos = jdos / np.prod(self._mesh) def _run_smearing_method(self): import anharmonic._phono3py as phono3c self.set_phonon(self._triplets_at_q.ravel()) f_max = np.max(self._frequencies) * 2 + self._sigma * 4 f_min = np.min(self._frequencies) * 2 - self._sigma * 4 self._set_frequency_points(f_min, f_max) jdos = np.zeros((len(self._frequency_points), 2), dtype='double') phono3c.joint_dos(jdos, self._frequency_points, self._triplets_at_q, self._weights_at_q, self._frequencies, self._sigma) jdos /= np.prod(self._mesh) self._joint_dos = jdos def _set_dynamical_matrix(self): self._dm = get_dynamical_matrix( self._fc2, self._supercell, self._primitive, nac_params=self._nac_params, frequency_scale_factor=self._frequency_scale_factor, symprec=self._symprec) def _set_triplets(self): if self._is_nosym: if self._log_level: print "Triplets at q without considering symmetry" sys.stdout.flush() (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map, map_triplets, map_q) = get_nosym_triplets_at_q( self._grid_point, self._mesh, self._reciprocal_lattice, with_bz_map=True) else: (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map, map_triplets, map_q) = get_triplets_at_q( self._grid_point, self._mesh, self._symmetry.get_pointgroup_operations(), self._reciprocal_lattice) def set_phonon(self, grid_points): set_phonon_c(self._dm, self._frequencies, self._eigenvectors, self._phonon_done, grid_points, self._grid_address, self._mesh, self._frequency_factor_to_THz, self._nac_q_direction, self._lapack_zheev_uplo) def _set_frequency_points(self, f_min, f_max): if self._num_frequency_points is None: if self._frequency_step is not None: self._frequency_points = np.arange( f_min, f_max, self._frequency_step, dtype='double') else: self._frequency_points = np.array(np.linspace( f_min, f_max, 201), dtype='double') else: self._frequency_points = np.array(np.linspace( f_min, f_max, self._num_frequency_points), dtype='double')
class Phono3py: def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, mesh=None, band_indices=None, sigmas=[], cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_nosym=False, symmetrize_fc3_q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): self._symprec = symprec self._sigmas = sigmas self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_nosym = is_nosym self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3_q = symmetrize_fc3_q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None # Linewidth (Imaginary part of self energy x 2) at temperatures self._linewidth = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None # Setup interaction self._interaction = None self._mesh = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._mesh = np.array(mesh, dtype='intc') if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band)] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack(self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, frequency_scale_factor=None): self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, frequency_factor_to_THz=self._frequency_factor_to_THz, cutoff_frequency=self._cutoff_frequency, is_nosym=self._is_nosym, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, is_permutation_symmetry=False, is_translational_symmetry=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(self._fc2) if is_translational_symmetry: set_translational_invariance(self._fc2) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero is_translational_symmetry=False, is_permutation_symmetry=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc3, disp_dataset['first_atoms']): disp1['forces'] = forces fc2 = get_fc2(self._supercell, self._symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(fc2) if is_translational_symmetry: set_translational_invariance(fc2) count = len(disp_dataset['first_atoms']) for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 self._fc3 = get_fc3( self._supercell, disp_dataset, fc2, self._symmetry, is_translational_symmetry=is_translational_symmetry, is_permutation_symmetry=is_permutation_symmetry, verbose=self._log_level) # Set fc3 elements zero beyond cutoff_distance if cutoff_distance: if self._log_level: print ("Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance) self.cutoff_fc3_by_zero(cutoff_distance) # Set fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance): cutoff_fc3_by_zero(self._fc3, self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self): if self._fc2 is not None: set_translational_invariance(self._fc2) if self._fc3 is not None: set_translational_invariance_fc3(self._fc3) def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 def get_primitive(self): return self._primitive def get_unitcell(self): return self._unitcell def get_supercell(self): return self._supercell def get_phonon_supercell(self): return self._phonon_supercell def get_phonon_primitive(self): return self._phonon_primitive def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset def get_displacement_dataset(self): return self._displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def run_imag_self_energy(self, grid_points, frequency_step=0.1, temperatures=[0.0, 300.0]): self._grid_points = grid_points self._temperatures = temperatures self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, temperatures=temperatures, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy(self._imag_self_energy, self._mesh, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, filename=filename) def run_linewidth(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double')): self._grid_points = grid_points self._temperatures = temperatures self._linewidth = get_linewidth(self._interaction, grid_points, self._sigmas, temperatures=temperatures, log_level=self._log_level) def write_linewidth(self, filename=None): write_linewidth(self._linewidth, self._band_indices, self._mesh, self._grid_points, self._sigmas, self._temperatures, filename=filename) def run_thermal_conductivity( self, is_LBTE=True, temperatures=np.arange(0, 1001, 10, dtype='double'), sigmas=[], mass_variances=None, grid_points=None, mesh_divisors=None, coarse_mesh_shifts=None, cutoff_lifetime=1e-4, # in second no_kappa_stars=False, gv_delta_q=None, # for group velocity write_gamma=False, read_gamma=False, write_collision=False, read_collision=False, write_amplitude=False, read_amplitude=False, input_filename=None, output_filename=None): if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, mass_variances=mass_variances, grid_points=grid_points, cutoff_lifetime=cutoff_lifetime, gv_delta_q=gv_delta_q, write_collision=write_collision, read_collision=read_collision, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, mass_variances=mass_variances, grid_points=grid_points, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, cutoff_lifetime=cutoff_lifetime, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, write_gamma=write_gamma, read_gamma=read_gamma, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift(self, grid_points, epsilon=0.1, temperatures=np.arange(0, 1001, 10, dtype='double'), output_filename=None): fst = FrequencyShift(self._interaction) for gp in grid_points: fst.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Frequency shift -o- ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) fst.run_interaction() fst.set_epsilon(epsilon) delta = np.zeros((len(temperatures), len(self._band_indices_flatten)), dtype='double') for i, t in enumerate(temperatures): fst.set_temperature(t) fst.run() delta[i] = fst.get_frequency_shift() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_frequency_shift(gp, bi, temperatures, delta[:, pos:(pos+len(bi))], self._mesh, epsilon=epsilon, filename=output_filename) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print ("Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec)
import numpy as np cell = read_vasp("POSCAR-unitcell") symmetry = Symmetry(cell, 1e-2) print symmetry.get_international_table() reciprocal_lattice = np.linalg.inv(cell.get_cell()) mesh = [7, 7, 7] print reciprocal_lattice (triplets_at_q, weights_at_q, grid_address, bz_map, triplets_map_at_q, ir_map_at_q)= get_triplets_at_q(74, mesh, symmetry.get_pointgroup_operations(), reciprocal_lattice, stores_triplets_map=True) for triplet in triplets_at_q: sum_q = (grid_address[triplet]).sum(axis=0) if (sum_q % mesh != 0).any(): print "============= Warning ==================" print triplet for tp in triplet: print grid_address[tp], np.linalg.norm(np.dot(reciprocal_lattice, grid_address[tp] / mesh)) print sum_q print "============= Warning =================="
class JointDos: def __init__(self, mesh, primitive, supercell, fc2, nac_params=None, sigma=None, frequency_step=0.1, frequency_factor_to_THz=VaspToTHz, frequency_scale_factor=1.0, is_nosym=False, symprec=1e-5, filename=None, log_level=False, lapack_zheev_uplo='L'): self._grid_point = None self._mesh = np.array(mesh, dtype='intc') self._primitive = primitive self._supercell = supercell self._fc2 = fc2 self._nac_params = nac_params self.set_sigma(sigma) self._frequency_step = frequency_step self._frequency_factor_to_THz = frequency_factor_to_THz self._frequency_scale_factor = frequency_scale_factor self._is_nosym = is_nosym self._symprec = symprec self._filename = filename self._log_level = log_level self._lapack_zheev_uplo = lapack_zheev_uplo self._num_band = self._primitive.get_number_of_atoms() * 3 self._reciprocal_lattice = np.linalg.inv(self._primitive.get_cell()) self._set_dynamical_matrix() self._symmetry = Symmetry(primitive, symprec) self._tetrahedron_method = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._nac_q_direction = None self._joint_dos = None self._frequency_points = None def run(self): try: import anharmonic._phono3py as phono3c self._run_c() except ImportError: print "Joint density of states in python is not implemented." return None, None def get_joint_dos(self): return self._joint_dos def get_frequency_points(self): return self._frequency_points def get_phonons(self): return self._frequencies, self._eigenvectors, self._phonon_done def set_nac_q_direction(self, nac_q_direction=None): if nac_q_direction is not None: self._nac_q_direction = np.array(nac_q_direction, dtype='double') def set_sigma(self, sigma): if sigma is None: self._sigma = None else: self._sigma = float(sigma) def set_grid_point(self, grid_point): self._grid_point = grid_point self._set_triplets() num_grid = np.prod(len(self._grid_address)) num_band = self._num_band if self._phonon_done is None: self._phonon_done = np.zeros(num_grid, dtype='byte') self._frequencies = np.zeros((num_grid, num_band), dtype='double') self._eigenvectors = np.zeros((num_grid, num_band, num_band), dtype='complex128') self._joint_dos = [] self._frequency_points = [] self._set_phonon(np.array([grid_point], dtype='intc')) def get_grid_address(self): return self._grid_address def get_triplets_at_q(self): return self._triplets_at_q, self._weights_at_q def _run_c(self, lang='Py'): if self._sigma is None: self._tetrahedron_method = TetrahedronMethod( self._reciprocal_lattice, mesh=self._mesh) if lang == 'C': self._run_c_tetrahedron_method() else: self._run_py_tetrahedron_method() else: self._run_smearing_method() def _run_c_tetrahedron_method(self): """ This is not very faster than _run_c_tetrahedron_method and use much more memory space. So this function is not set as default. """ import anharmonic._phono3py as phono3c thm = self._tetrahedron_method unique_vertices = thm.get_unique_tetrahedra_vertices() for i, j in zip((1, 2), (1, -1)): neighboring_grid_points = np.zeros( len(unique_vertices) * len(self._triplets_at_q), dtype='intc') phono3c.neighboring_grid_points( neighboring_grid_points, self._triplets_at_q[:, i].flatten(), j * unique_vertices, self._mesh, self._grid_address, self._bz_map) self._set_phonon(np.unique(neighboring_grid_points)) f_max = np.max(self._frequencies) * 2 + self._frequency_step / 10 f_min = np.min(self._frequencies) * 2 frequency_points = np.arange(f_min, f_max, self._frequency_step, dtype='double') num_band = self._num_band num_triplets = len(self._triplets_at_q) num_freq_points = len(frequency_points) g = np.zeros((num_triplets, num_freq_points, num_band, num_band, 2), dtype='double') phono3c.triplets_integration_weights( g, frequency_points, thm.get_tetrahedra(), self._mesh, self._triplets_at_q, self._frequencies, self._grid_address, self._bz_map) jdos = np.tensordot(g, self._weights_at_q, axes=([0, 0])) jdos = jdos.sum(axis=1).sum(axis=1)[:, 0] self._joint_dos = jdos / np.prod(self._mesh) self._frequency_points = frequency_points def _run_py_tetrahedron_method(self): self._vertices = get_tetrahedra_vertices( self._tetrahedron_method.get_tetrahedra(), self._mesh, self._triplets_at_q, self._grid_address, self._bz_map) self._set_phonon(self._vertices.ravel()) thm = self._tetrahedron_method f_max = np.max(self._frequencies) * 2 + self._frequency_step / 10 f_min = np.min(self._frequencies) * 2 freq_points = np.arange(f_min, f_max, self._frequency_step, dtype='double') jdos = np.zeros_like(freq_points) for vertices, w in zip(self._vertices, self._weights_at_q): for i, j in list(np.ndindex(self._num_band, self._num_band)): f1 = self._frequencies[vertices[0], i] f2 = self._frequencies[vertices[1], j] thm.set_tetrahedra_omegas(f1 + f2) thm.run(freq_points) iw = thm.get_integration_weight() jdos += iw * w self._joint_dos = jdos / np.prod(self._mesh) self._frequency_points = freq_points def _run_smearing_method(self): import anharmonic._phono3py as phono3c self._set_phonon(self._triplets_at_q.ravel()) f_max = np.max(self._frequencies) * 2 + self._sigma * 4 f_min = np.min(self._frequencies) * 2 - self._sigma * 4 freq_points = np.arange(f_min, f_max, self._frequency_step, dtype='double') jdos = np.zeros_like(freq_points) phono3c.joint_dos(jdos, freq_points, self._triplets_at_q, self._weights_at_q, self._frequencies, self._sigma) jdos /= np.prod(self._mesh) self._joint_dos = jdos self._frequency_points = freq_points def _set_dynamical_matrix(self): self._dm = get_dynamical_matrix( self._fc2, self._supercell, self._primitive, nac_params=self._nac_params, frequency_scale_factor=self._frequency_scale_factor, symprec=self._symprec) def _set_triplets(self): if self._is_nosym: if self._log_level: print "Triplets at q without considering symmetry" sys.stdout.flush() (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map) = get_nosym_triplets_at_q( self._grid_point, self._mesh, self._reciprocal_lattice, with_bz_map=True) else: (self._triplets_at_q, self._weights_at_q, self._grid_address, self._bz_map) = get_triplets_at_q( self._grid_point, self._mesh, self._symmetry.get_pointgroup_operations(), self._reciprocal_lattice) def _set_phonon(self, grid_points): set_phonon_c(self._dm, self._frequencies, self._eigenvectors, self._phonon_done, grid_points, self._grid_address, self._mesh, self._frequency_factor_to_THz, self._nac_q_direction, self._lapack_zheev_uplo)
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 PhonopyUnfolding(Phonopy): """ unitcell: before symmetrization """ def __init__(self, unitcell, unitcell_ideal, supercell_matrix, primitive_matrix_ideal, nac_params=None, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, dynamical_matrix_decimals=None, force_constants_decimals=None, star="none", mode="eigenvector", symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): self._symprec = symprec self._distance = distance self._factor = factor self._is_auto_displacements = is_auto_displacements self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._unitcell_ideal = unitcell_ideal self._supercell_matrix = supercell_matrix self._primitive_matrix = None self._primitive_matrix_ideal = primitive_matrix_ideal self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() self._build_supercell_ideal() self._build_primitive_cell_ideal() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_symmetry_ideal() self._search_primitive_symmetry_ideal() # 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 self._star = star self._mode = mode # Single point def run_single_point(self, qpoint, distance): SinglePoint( qpoint, distance, dynamical_matrix=self._dynamical_matrix, unitcell_ideal=self._unitcell_ideal, primitive_matrix_ideal=self._primitive_matrix_ideal, factor=self._factor, star=self._star, mode=self._mode, verbose=True) # 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, self._unitcell_ideal, self._primitive_matrix_ideal, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor, star=self._star, mode=self._mode, verbose=True) return True # 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 # TODO(ikeda): Check how "rotations" works. self._mesh = MeshUnfolding( self._dynamical_matrix, self._unitcell_ideal, self._primitive_matrix_ideal, 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, star=self._star, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor, use_lapack_solver=self._use_lapack_solver, mode=self._mode) return True # 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 = TotalDosUnfolding( 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 _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: raise ValueError( 'Currently NAC is not available for unfolding.') return True def _search_symmetry_ideal(self): self._symmetry = Symmetry(self._supercell_ideal, self._symprec, self._is_symmetry) def _search_primitive_symmetry_ideal(self): self._primitive_symmetry = Symmetry(self._primitive_ideal, self._symprec, self._is_symmetry) n0 = len(self._symmetry.get_pointgroup_operations()) n1 = len(self._primitive_symmetry.get_pointgroup_operations()) if n0 != n1: raise Warning("Point group symmetries of supercell and primitive" "cell are different.") def _build_supercell_ideal(self): self._supercell_ideal = get_supercell( self._unitcell_ideal, self._supercell_matrix, self._symprec) def _build_primitive_cell_ideal(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_ideal is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix_ideal) self._primitive_ideal = get_primitive( self._supercell_ideal, trans_mat, self._symprec) num_satom = self._supercell_ideal.get_number_of_atoms() num_patom = self._primitive_ideal.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False def average_masses(self): masses = self._unitcell.get_masses() masses_average = calculate_average_masses( masses, Symmetry(self._unitcell_ideal)) self._unitcell.set_masses(masses_average) self._build_supercell() self._build_primitive_cell() self._search_symmetry() self._search_primitive_symmetry() def average_force_constants(self): fc_symmetrizer_spg = FCSymmetrizerSPG( force_constants=self._force_constants, atoms=self._unitcell, atoms_ideal=self._unitcell_ideal, supercell_matrix=self._supercell_matrix, ) fc_symmetrizer_spg.average_force_constants_spg() fc_symmetrizer_spg.write_force_constants_symmetrized() fc_average = fc_symmetrizer_spg.get_force_constants_symmetrized() self.set_force_constants(fc_average) # Dynamical matrices are also prepared inside.
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 Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3q = symmetrize_fc3q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix if type(primitive_matrix) is str and primitive_matrix == 'auto': self._primitive_matrix = self._guess_primitive_matrix() else: self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh_numbers = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._set_mesh_numbers(mesh) self.set_band_indices(band_indices) def set_band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack( self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None, solve_dynamical_matrices=True): if self._mesh_numbers is None: print("'mesh' has to be set in Phono3py instantiation.") raise RuntimeError self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh_numbers, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, frequency_scale_factor=frequency_scale_factor, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3q=self._symmetrize_fc3q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, solve_dynamical_matrices=solve_dynamical_matrices, verbose=self._log_level) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def get_phonon_data(self): if self._interaction is not None: grid_address = self._interaction.get_grid_address() freqs, eigvecs, _ = self._interaction.get_phonons() return freqs, eigvecs, grid_address else: msg = "set_phph_interaction has to be done." raise RuntimeError(msg) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: # 'is_diagonal=False' below is made intentionally. For # third-order force constants, we need better accuracy, # and I expect this choice is better for it, but not very # sure. # In phono3py, two atoms are displaced for each # configuration and the displacements are chosen, first # displacement from the perfect supercell, then second # displacement, considering symmetry. If I choose # is_diagonal=False for the first displacement, the # symmetry is less broken and the number of second # displacements can be smaller than in the case of # is_diagonal=True for the first displacement. This is # done in the call get_least_displacements() in # phonon3.displacement_fc3.get_third_order_displacements(). # # The call get_least_displacements() is only for the # second order force constants, but 'is_diagonal=False' to # be consistent with the above function call, and also for # the accuracy when calculating ph-ph interaction # strength because displacement directions are better to be # close to perpendicular each other to fit force constants. # # On the discussion of the accuracy, these are just my # expectation when I designed phono3py in the early time, # and in fact now I guess not very different. If these are # little different, then I should not surprise users to # change the default behaviour. At this moment, this is # open question and we will have more advance and should # have better specificy external software on this. phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = directions_to_displacement_dataset( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, symmetrize_fc2=False, is_compact_fc=False, use_alm=False, alm_options=None): if displacement_dataset is None: if self._phonon_displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = self._phonon_displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces if is_compact_fc: p2s_map = self._phonon_primitive.p2s_map else: p2s_map = None if use_alm: from phonopy.interface.alm import get_fc2 as get_fc2_alm self._fc2 = get_fc2_alm(self._phonon_supercell, self._phonon_primitive, disp_dataset, atom_list=p2s_map, alm_options=alm_options, log_level=self._log_level) else: self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset, atom_list=p2s_map) if symmetrize_fc2: if is_compact_fc: symmetrize_compact_force_constants( self._fc2, self._phonon_primitive) else: symmetrize_force_constants(self._fc2) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero symmetrize_fc3r=False, is_compact_fc=False, use_alm=False, alm_options=None): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if use_alm: from phono3py.other.alm_wrapper import get_fc3 as get_fc3_alm fc2, fc3 = get_fc3_alm(self._supercell, self._primitive, forces_fc3, disp_dataset, self._symmetry, alm_options=alm_options, is_compact_fc=is_compact_fc, log_level=self._log_level) else: fc2, fc3 = self._get_fc3(forces_fc3, disp_dataset, is_compact_fc=is_compact_fc) if symmetrize_fc3r: if is_compact_fc: set_translational_invariance_compact_fc3( fc3, self._primitive) set_permutation_symmetry_compact_fc3(fc3, self._primitive) if self._fc2 is None: symmetrize_compact_force_constants(fc2, self._primitive) else: set_translational_invariance_fc3(fc3) set_permutation_symmetry_fc3(fc3) if self._fc2 is None: symmetrize_force_constants(fc2) # Set fc2 and fc3 self._fc3 = fc3 # Normally self._fc2 is overwritten in produce_fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero(_fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self): if self._fc2 is not None: set_translational_invariance(self._fc2) if self._fc3 is not None: set_translational_invariance_fc3(self._fc3) @property def version(self): return __version__ def get_version(self): return self.version def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 @property def nac_params(self): return self._nac_params def get_nac_params(self): return self.nac_params @property def primitive(self): return self._primitive def get_primitive(self): return self.primitive @property def unitcell(self): return self._unitcell def get_unitcell(self): return self.unitcell @property def supercell(self): return self._supercell def get_supercell(self): return self.supercell @property def phonon_supercell(self): return self._phonon_supercell def get_phonon_supercell(self): return self.phonon_supercell @property def phonon_primitive(self): return self._phonon_primitive def get_phonon_primitive(self): return self.phonon_primitive @property def symmetry(self): """return symmetry of supercell""" return self._symmetry def get_symmetry(self): return self.symmetry @property def primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self.primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry @property def supercell_matrix(self): return self._supercell_matrix def get_supercell_matrix(self): return self.supercell_matrix @property def phonon_supercell_matrix(self): return self._phonon_supercell_matrix def get_phonon_supercell_matrix(self): return self.phonon_supercell_matrix @property def primitive_matrix(self): return self._primitive_matrix def get_primitive_matrix(self): return self.primitive_matrix @property def unit_conversion_factor(self): return self._frequency_factor_to_THz def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset @property def displacement_dataset(self): return self._displacement_dataset def get_displacement_dataset(self): return self.displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements @property def mesh_numbers(self): return self._mesh_numbers def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False, output_filename=None): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, output_filename=output_filename, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh_numbers, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre solve_collective_phonon=False, use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, compression=None, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, solve_collective_phonon=solve_collective_phonon, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_gamma_detail=write_gamma_detail, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift( self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), epsilons=None, output_filename=None): """Frequency shift from lowest order diagram is calculated. Args: epslins(list of float): The value to avoid divergence. When multiple values are given frequency shifts for those values are returned. """ if self._interaction is None: self.set_phph_interaction() if epsilons is None: _epsilons = [0.1] else: _epsilons = epsilons self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, _epsilons, temperatures, output_filename=output_filename, log_level=self._log_level) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if (self._primitive is not None and (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any()): print(" Primitive cells for fc2 and fc3 can be different.") raise RuntimeError def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _guess_primitive_matrix(self): return guess_primitive_matrix(self._unitcell, symprec=self._symprec) def _set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[ [p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map()]] self._phonon_supercell.set_masses(s_masses) def _set_mesh_numbers(self, mesh): _mesh = np.array(mesh) mesh_nums = None if _mesh.shape: if _mesh.shape == (3,): mesh_nums = mesh elif self._primitive_symmetry is None: mesh_nums = length2mesh(mesh, self._primitive.get_cell()) else: rotations = self._primitive_symmetry.get_pointgroup_operations() mesh_nums = length2mesh(mesh, self._primitive.get_cell(), rotations=rotations) if mesh_nums is None: msg = "mesh has inappropriate type." raise TypeError(msg) self._mesh_numbers = mesh_nums def _get_fc3(self, forces_fc3, disp_dataset, is_compact_fc=False): count = 0 for disp1 in disp_dataset['first_atoms']: disp1['forces'] = forces_fc3[count] count += 1 for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 fc2, fc3 = get_fc3(self._supercell, self._primitive, disp_dataset, self._symmetry, is_compact_fc=is_compact_fc, verbose=self._log_level) return fc2, fc3
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
def get_born_OUTCAR(poscar_filename="POSCAR", outcar_filename="OUTCAR", primitive_axis=np.eye(3), is_symmetry=True, symmetrize_tensors=False): cell = read_vasp(poscar_filename) primitive = Primitive(cell, primitive_axis) p2p = primitive.get_primitive_to_primitive_map() symmetry = Symmetry(primitive, is_symmetry=is_symmetry) independent_atoms = symmetry.get_independent_atoms() prim_lat = primitive.get_cell().T outcar = open(outcar_filename) borns = [] while True: line = outcar.readline() if not line: break if "NIONS" in line: num_atom = int(line.split()[11]) if "MACROSCOPIC STATIC DIELECTRIC TENSOR" in line: epsilon = [] outcar.readline() epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) if "BORN" in line: outcar.readline() line = outcar.readline() if "ion" in line: for i in range(num_atom): born = [] born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) outcar.readline() borns.append(born) reduced_borns = [] for p_i, u_i in enumerate(p2p): if p_i in independent_atoms: if symmetrize_tensors: site_sym = [ similarity_transformation(prim_lat, rot) for rot in symmetry.get_site_symmetry(p_i) ] reduced_borns.append(symmetrize_tensor(borns[u_i], site_sym)) else: reduced_borns.append(borns[u_i]) if symmetrize_tensors: point_sym = [ similarity_transformation(prim_lat, rot) for rot in symmetry.get_pointgroup_operations() ] epsilon = symmetrize_tensor(epsilon, point_sym) else: epsilon = np.array(epsilon) return np.array(reduced_borns), epsilon
class Phono3py: def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_nosym=False, symmetrize_fc3_q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: sigmas = [] self._symprec = symprec self._sigmas = sigmas self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_nosym = is_nosym self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3_q = symmetrize_fc3_q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None # Linewidth (Imaginary part of self energy x 2) at temperatures self._linewidth = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None # Setup interaction self._interaction = None self._mesh = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._mesh = np.array(mesh, dtype='intc') self.set_band_indices(band_indices) def set_band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack(self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None): self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_nosym=self._is_nosym, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, is_permutation_symmetry=False, translational_symmetry_type=0): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(self._fc2) if translational_symmetry_type: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) def produce_fc3(self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero translational_symmetry_type=0, is_permutation_symmetry=False, is_permutation_symmetry_fc2=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc3, disp_dataset['first_atoms']): disp1['forces'] = forces fc2 = get_fc2(self._supercell, self._symmetry, disp_dataset) if is_permutation_symmetry_fc2: set_permutation_symmetry(fc2) if translational_symmetry_type: set_translational_invariance( fc2, translational_symmetry_type=translational_symmetry_type) count = len(disp_dataset['first_atoms']) for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 self._fc3 = get_fc3( self._supercell, disp_dataset, fc2, self._symmetry, translational_symmetry_type=translational_symmetry_type, is_permutation_symmetry=is_permutation_symmetry, verbose=self._log_level) # Set fc3 elements zero beyond cutoff_distance if cutoff_distance: if self._log_level: print("Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance) self.cutoff_fc3_by_zero(cutoff_distance) # Set fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance): cutoff_fc3_by_zero(self._fc3, self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self, translational_symmetry_type=1): if self._fc2 is not None: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) if self._fc3 is not None: set_translational_invariance_fc3( self._fc3, translational_symmetry_type=translational_symmetry_type) def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 def get_primitive(self): return self._primitive def get_unitcell(self): return self._unitcell def get_supercell(self): return self._supercell def get_phonon_supercell(self): return self._phonon_supercell def get_phonon_primitive(self): return self._phonon_primitive def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset def get_displacement_dataset(self): return self._displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, run_with_g=True, write_details=False): if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, run_with_g=run_with_g, write_details=write_details, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename) def run_linewidth(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), run_with_g=True, write_details=False): self._grid_points = grid_points self._temperatures = temperatures self._linewidth = get_linewidth(self._interaction, grid_points, self._sigmas, temperatures=temperatures, run_with_g=run_with_g, write_details=write_details, log_level=self._log_level) def write_linewidth(self, filename=None): write_linewidth(self._linewidth, self._band_indices, self._mesh, self._grid_points, self._sigmas, self._temperatures, filename=filename) def run_thermal_conductivity( self, is_LBTE=True, temperatures=np.arange(0, 1001, 10, dtype='double'), sigmas=None, is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre use_averaged_pp_interaction=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, no_kappa_stars=False, gv_delta_q=None, # for group velocity run_with_g=True, # integration weights for smearing method, too pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, write_collision=False, read_collision=False, write_amplitude=False, read_amplitude=False, input_filename=None, output_filename=None): if sigmas is None: sigmas = [] if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, is_reducible_collision_matrix=is_reducible_collision_matrix, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, pinv_cutoff=pinv_cutoff, write_collision=write_collision, read_collision=read_collision, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_averaged_pp_interaction=use_averaged_pp_interaction, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, run_with_g=run_with_g, write_gamma=write_gamma, read_gamma=read_gamma, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift(self, grid_points, epsilons=None, temperatures=np.arange(0, 1001, 10, dtype='double'), output_filename=None): if epsilons is None: epsilons = [0.1] self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, epsilons, temperatures, output_filename=output_filename, log_level=self._log_level) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len(self._primitive_symmetry.get_pointgroup_operations())): print("Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell( self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if self._primitive is not None: if (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any(): print("********************* Warning *********************") print(" Primitive cells for fc2 and fc3 can be different.") print("********************* Warning *********************") def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append(Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map()]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[ [p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map()]] self._phonon_supercell.set_masses(s_masses)
class PhonopyUnfolding(Phonopy): """ unitcell: before symmetrization """ def __init__(self, unitcell, unitcell_ideal, supercell_matrix, primitive_matrix_ideal, nac_params=None, distance=0.01, factor=VaspToTHz, is_auto_displacements=True, dynamical_matrix_decimals=None, force_constants_decimals=None, star="none", mode="eigenvector", symprec=1e-5, is_symmetry=True, use_lapack_solver=False, log_level=0): self._symprec = symprec self._distance = distance self._factor = factor self._is_auto_displacements = is_auto_displacements self._is_symmetry = is_symmetry self._use_lapack_solver = use_lapack_solver self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._unitcell_ideal = unitcell_ideal self._supercell_matrix = supercell_matrix self._primitive_matrix = None if type(primitive_matrix_ideal) is str and primitive_matrix_ideal == 'auto': self._primitive_matrix_ideal = self._guess_primitive_matrix() elif primitive_matrix is not None: self._primitive_matrix_ideal = np.array(primitive_matrix_ideal, dtype='double', order='c') else: self._primitive_matrix = None self._supercell = None self._primitive = None self._build_supercell() self._build_primitive_cell() self._build_supercell_ideal() self._build_primitive_cell_ideal() # Set supercell and primitive symmetry self._symmetry = None self._primitive_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_symmetry_ideal() self._search_primitive_symmetry_ideal() # 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 self._star = star self._mode = mode # Single point def run_single_point(self, qpoint, distance): SinglePoint( qpoint, distance, dynamical_matrix=self._dynamical_matrix, unitcell_ideal=self._unitcell_ideal, primitive_matrix_ideal=self._primitive_matrix_ideal, factor=self._factor, star=self._star, mode=self._mode, verbose=True) # 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, self._unitcell_ideal, self._primitive_matrix_ideal, is_eigenvectors=is_eigenvectors, is_band_connection=is_band_connection, group_velocity=self._group_velocity, factor=self._factor, star=self._star, mode=self._mode, verbose=True) return True # 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 # TODO(ikeda): Check how "rotations" works. self._mesh = MeshUnfolding( self._dynamical_matrix, self._unitcell_ideal, self._primitive_matrix_ideal, 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, star=self._star, group_velocity=self._group_velocity, rotations=self._primitive_symmetry.get_pointgroup_operations(), factor=self._factor, use_lapack_solver=self._use_lapack_solver, mode=self._mode) return True # 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 = TotalDosUnfolding( 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 _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 def _search_symmetry_ideal(self): self._symmetry = Symmetry(self._supercell_ideal, self._symprec, self._is_symmetry) def _search_primitive_symmetry_ideal(self): self._primitive_symmetry = Symmetry(self._primitive_ideal, self._symprec, self._is_symmetry) n0 = len(self._symmetry.get_pointgroup_operations()) n1 = len(self._primitive_symmetry.get_pointgroup_operations()) if n0 != n1: raise Warning("Point group symmetries of supercell and primitive" "cell are different.") def _build_supercell_ideal(self): self._supercell_ideal = get_supercell( self._unitcell_ideal, self._supercell_matrix, self._symprec) def _build_primitive_cell_ideal(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_ideal is None: trans_mat = inv_supercell_matrix else: trans_mat = np.dot(inv_supercell_matrix, self._primitive_matrix_ideal) self._primitive_ideal = get_primitive( self._supercell_ideal, trans_mat, self._symprec) num_satom = self._supercell_ideal.get_number_of_atoms() num_patom = self._primitive_ideal.get_number_of_atoms() if abs(num_satom * np.linalg.det(trans_mat) - num_patom) < 0.1: return True else: return False def average_masses(self): masses = self._unitcell.get_masses() masses_average = calculate_average_masses( masses, Symmetry(self._unitcell_ideal)) self._unitcell.set_masses(masses_average) self._build_supercell() self._build_primitive_cell() self._search_symmetry() self._search_primitive_symmetry() def _guess_primitive_matrix(self): return guess_primitive_matrix(self._unitcell_ideal, symprec=self._symprec) def average_force_constants(self): fc_symmetrizer_spg = FCSymmetrizerSPG( force_constants=self._force_constants, atoms=self._unitcell, atoms_ideal=self._unitcell_ideal, supercell_matrix=self._supercell_matrix, ) fc_symmetrizer_spg.average_force_constants_spg() fc_symmetrizer_spg.write_force_constants_symmetrized() fc_average = fc_symmetrizer_spg.get_force_constants_symmetrized() self.set_force_constants(fc_average) # Dynamical matrices are also prepared inside.
def get_born_OUTCAR(poscar_filename="POSCAR", outcar_filename="OUTCAR", primitive_axis=np.eye(3), supercell_matrix=np.eye(3, dtype='intc'), is_symmetry=True, symmetrize_tensors=False): ucell = read_vasp(poscar_filename) scell = get_supercell(ucell, supercell_matrix) inv_smat = np.linalg.inv(supercell_matrix) pcell = get_primitive(scell, np.dot(inv_smat, primitive_axis)) u_sym = Symmetry(ucell, is_symmetry=is_symmetry) p_sym = Symmetry(pcell, is_symmetry=is_symmetry) lattice = ucell.get_cell().T outcar = open(outcar_filename) borns = [] while True: line = outcar.readline() if not line: break if "NIONS" in line: num_atom = int(line.split()[11]) if "MACROSCOPIC STATIC DIELECTRIC TENSOR" in line: epsilon = [] outcar.readline() epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) epsilon.append([float(x) for x in outcar.readline().split()]) if "BORN" in line: outcar.readline() line = outcar.readline() if "ion" in line: for i in range(num_atom): born = [] born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) born.append([float(x) for x in outcar.readline().split()][1:]) outcar.readline() borns.append(born) borns = np.array(borns, dtype='double') epsilon = np.array(epsilon, dtype='double') if symmetrize_tensors: borns_orig = borns.copy() point_sym = [similarity_transformation(lattice, r) for r in u_sym.get_pointgroup_operations()] epsilon = symmetrize_tensor(epsilon, point_sym) for i in range(num_atom): z = borns[i] site_sym = [similarity_transformation(lattice, r) for r in u_sym.get_site_symmetry(i)] borns[i] = symmetrize_tensor(z, site_sym) rotations = u_sym.get_symmetry_operations()['rotations'] map_atoms = u_sym.get_map_atoms() borns_copy = np.zeros_like(borns) for i, m_i in enumerate(map_atoms): count = 0 for j, r_j in enumerate(u_sym.get_map_operations()): if map_atoms[j] == m_i: count += 1 r_cart = similarity_transformation(lattice, rotations[r_j]) borns_copy[i] += similarity_transformation(r_cart, borns[j]) borns_copy[i] /= count borns = borns_copy sum_born = borns.sum(axis=0) / len(borns) borns -= sum_born if (np.abs(borns_orig - borns) > 0.1).any(): sys.stderr.write( "Born effective charge symmetrization might go wrong.\n") p2s = np.array(pcell.get_primitive_to_supercell_map(), dtype='intc') s_indep_atoms = p2s[p_sym.get_independent_atoms()] u2u = scell.get_unitcell_to_unitcell_map() u_indep_atoms = [u2u[x] for x in s_indep_atoms] reduced_borns = borns[u_indep_atoms].copy() return reduced_borns, epsilon
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
class Phono3py: def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, mesh=None, band_indices=None, sigmas=[], cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_nosym=False, symmetrize_fc3_q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): self._symprec = symprec self._sigmas = sigmas self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_nosym = is_nosym self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3_q = symmetrize_fc3_q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None # Linewidth (Imaginary part of self energy x 2) at temperatures self._linewidth = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None # Setup interaction self._interaction = None self._mesh = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._mesh = np.array(mesh, dtype='intc') if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band)] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack( self._band_indices).astype('intc') def set_phph_interaction(self, nac_params=None, nac_q_direction=None, use_Peierls_model=False, frequency_scale_factor=None): self._interaction = Interaction( self._supercell, self._primitive, self._mesh, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, use_Peierls_model=use_Peierls_model, frequency_factor_to_THz=self._frequency_factor_to_THz, cutoff_frequency=self._cutoff_frequency, is_nosym=self._is_nosym, symmetrize_fc3_q=self._symmetrize_fc3_q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = direction_to_displacement_fc2( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2, displacement_dataset=None, is_permutation_symmetry=False, translational_symmetry_type=0): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc2, disp_dataset['first_atoms']): disp1['forces'] = forces self._fc2 = get_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(self._fc2) if translational_symmetry_type: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) def produce_fc3( self, forces_fc3, displacement_dataset=None, cutoff_distance=None, # set fc3 zero translational_symmetry_type=0, is_permutation_symmetry=False): if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset for forces, disp1 in zip(forces_fc3, disp_dataset['first_atoms']): disp1['forces'] = forces fc2 = get_fc2(self._supercell, self._symmetry, disp_dataset) if is_permutation_symmetry: set_permutation_symmetry(fc2) if translational_symmetry_type: set_translational_invariance( fc2, translational_symmetry_type=translational_symmetry_type) count = len(disp_dataset['first_atoms']) for disp1 in disp_dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['delta_forces'] = forces_fc3[count] - disp1['forces'] count += 1 self._fc3 = get_fc3( self._supercell, disp_dataset, fc2, self._symmetry, translational_symmetry_type=translational_symmetry_type, is_permutation_symmetry=is_permutation_symmetry, verbose=self._log_level) # Set fc3 elements zero beyond cutoff_distance if cutoff_distance: if self._log_level: print("Cutting-off fc3 by zero (cut-off distance: %f)" % cutoff_distance) self.cutoff_fc3_by_zero(cutoff_distance) # Set fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance): cutoff_fc3_by_zero(self._fc3, self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self, translational_symmetry_type=1): if self._fc2 is not None: set_translational_invariance( self._fc2, translational_symmetry_type=translational_symmetry_type) if self._fc3 is not None: set_translational_invariance_fc3( self._fc3, translational_symmetry_type=translational_symmetry_type) def get_interaction_strength(self): return self._interaction def get_fc2(self): return self._fc2 def set_fc2(self, fc2): self._fc2 = fc2 def get_fc3(self): return self._fc3 def set_fc3(self, fc3): self._fc3 = fc3 def get_primitive(self): return self._primitive def get_unitcell(self): return self._unitcell def get_supercell(self): return self._supercell def get_phonon_supercell(self): return self._phonon_supercell def get_phonon_primitive(self): return self._phonon_primitive def get_symmetry(self): """return symmetry of supercell""" return self._symmetry def get_primitive_symmetry(self): return self._primitive_symmetry def get_phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset def get_displacement_dataset(self): return self._displacement_dataset def get_phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=[0.0, 300.0], scattering_event_class=None): self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename) def run_linewidth(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double')): self._grid_points = grid_points self._temperatures = temperatures self._linewidth = get_linewidth(self._interaction, grid_points, self._sigmas, temperatures=temperatures, log_level=self._log_level) def write_linewidth(self, filename=None): write_linewidth(self._linewidth, self._band_indices, self._mesh, self._grid_points, self._sigmas, self._temperatures, filename=filename) def run_thermal_conductivity( self, is_LBTE=True, temperatures=np.arange(0, 1001, 10, dtype='double'), sigmas=[], is_isotope=False, mass_variances=None, grid_points=None, mesh_divisors=None, coarse_mesh_shifts=None, cutoff_mfp=None, # in micrometre is_reducible_collision_matrix=False, no_kappa_stars=False, gv_delta_q=None, # for group velocity pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, write_collision=False, read_collision=False, write_amplitude=False, read_amplitude=False, input_filename=None, output_filename=None): if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, cutoff_mfp=cutoff_mfp, is_reducible_collision_matrix=is_reducible_collision_matrix, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, pinv_cutoff=pinv_cutoff, write_collision=write_collision, read_collision=read_collision, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, cutoff_mfp=cutoff_mfp, no_kappa_stars=no_kappa_stars, gv_delta_q=gv_delta_q, write_gamma=write_gamma, read_gamma=read_gamma, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_thermal_conductivity(self): return self._thermal_conductivity def get_frequency_shift(self, grid_points, epsilon=0.1, temperatures=np.arange(0, 1001, 10, dtype='double'), output_filename=None): fst = FrequencyShift(self._interaction) for gp in grid_points: fst.set_grid_point(gp) if self._log_level: weights = self._interaction.get_triplets_at_q()[1] print "------ Frequency shift -o- ------" print "Number of ir-triplets:", print "%d / %d" % (len(weights), weights.sum()) fst.run_interaction() fst.set_epsilon(epsilon) delta = np.zeros( (len(temperatures), len(self._band_indices_flatten)), dtype='double') for i, t in enumerate(temperatures): fst.set_temperature(t) fst.run() delta[i] = fst.get_frequency_shift() for i, bi in enumerate(self._band_indices): pos = 0 for j in range(i): pos += len(self._band_indices[j]) write_frequency_shift(gp, bi, temperatures, delta[:, pos:(pos + len(bi))], self._mesh, epsilon=epsilon, filename=output_filename) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len( self._primitive_symmetry.get_pointgroup_operations())): print( "Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell(self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec)
class Phono3py(object): def __init__(self, unitcell, supercell_matrix, primitive_matrix=None, phonon_supercell_matrix=None, masses=None, mesh=None, band_indices=None, sigmas=None, sigma_cutoff=None, cutoff_frequency=1e-4, frequency_factor_to_THz=VaspToTHz, is_symmetry=True, is_mesh_symmetry=True, symmetrize_fc3q=False, symprec=1e-5, log_level=0, lapack_zheev_uplo='L'): if sigmas is None: self._sigmas = [None] else: self._sigmas = sigmas self._sigma_cutoff = sigma_cutoff self._symprec = symprec self._frequency_factor_to_THz = frequency_factor_to_THz self._is_symmetry = is_symmetry self._is_mesh_symmetry = is_mesh_symmetry self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetrize_fc3q = symmetrize_fc3q self._cutoff_frequency = cutoff_frequency self._log_level = log_level # Create supercell and primitive cell self._unitcell = unitcell self._supercell_matrix = supercell_matrix if type(primitive_matrix) is str and primitive_matrix == 'auto': self._primitive_matrix = self._guess_primitive_matrix() else: self._primitive_matrix = primitive_matrix self._phonon_supercell_matrix = phonon_supercell_matrix # optional self._supercell = None self._primitive = None self._phonon_supercell = None self._phonon_primitive = None self._build_supercell() self._build_primitive_cell() self._build_phonon_supercell() self._build_phonon_primitive_cell() if masses is not None: self._set_masses(masses) # Set supercell, primitive, and phonon supercell symmetries self._symmetry = None self._primitive_symmetry = None self._phonon_supercell_symmetry = None self._search_symmetry() self._search_primitive_symmetry() self._search_phonon_supercell_symmetry() # Displacements and supercells self._supercells_with_displacements = None self._displacement_dataset = None self._phonon_displacement_dataset = None self._phonon_supercells_with_displacements = None # Thermal conductivity self._thermal_conductivity = None # conductivity_RTA object # Imaginary part of self energy at frequency points self._imag_self_energy = None self._scattering_event_class = None self._grid_points = None self._frequency_points = None self._temperatures = None # Other variables self._fc2 = None self._fc3 = None self._nac_params = None # Setup interaction self._interaction = None self._mesh_numbers = None self._band_indices = None self._band_indices_flatten = None if mesh is not None: self._set_mesh_numbers(mesh) self.set_band_indices(band_indices) @property def thermal_conductivity(self): return self._thermal_conductivity def get_thermal_conductivity(self): return self.thermal_conductivity @property def displacements(self): """Return displacements Returns ------- displacements : ndarray Displacements of all atoms of all supercells in Cartesian coordinates. shape=(supercells, natom, 3), dtype='double', order='C' """ dataset = self._displacement_dataset if 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) for disp1 in dataset['first_atoms']: num_scells += len(disp1['second_atoms']) displacements = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') i = 0 for disp1 in dataset['first_atoms']: displacements[i, disp1['number']] = disp1['displacement'] i += 1 for disp1 in dataset['first_atoms']: for disp2 in dataset['second_atoms']: displacements[i, disp2['number']] = disp2['displacement'] i += 1 elif 'forces' in dataset or 'displacements' in dataset: displacements = dataset['displacements'] else: raise RuntimeError("displacement dataset has wrong format.") return displacements @displacements.setter def displacements(self, displacements): """Set displacemens Parameters ---------- displacemens : array_like Atomic displacements of all atoms of all supercells. Only type2 displacement dataset is supported, i.e., displacements has to have the array shape of (supercells, natom, 3). When displacement dataset is in type1, the type2 dataset is created and force information is lost. """ dataset = self._displacement_dataset disps = np.array(displacements, dtype='double', order='C') natom = self._supercell.get_number_of_atoms() if (disps.ndim != 3 or disps.shape[1:] != (natom, 3)): raise RuntimeError("Array shape of displacements is incorrect.") if 'first_atoms' in dataset: dataset = {'displacements': disps} elif 'displacements' in dataset or 'forces' in dataset: dataset['displacements'] = disps @property def forces(self): dataset = self._displacement_dataset if 'forces' in dataset: return dataset['forces'] elif 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) for disp1 in dataset['first_atoms']: num_scells += len(disp1['second_atoms']) forces = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') i = 0 for disp1 in dataset['first_atoms']: forces[i] = disp1['forces'] i += 1 for disp1 in dataset['first_atoms']: for disp2 in disp1['second_atoms']: forces[i] = disp2['forces'] i += 1 return forces else: raise RuntimeError("displacement dataset has wrong format.") @forces.setter def forces(self, forces_fc3): """Set forces in displacement dataset. Parameters ---------- forces_fc3 : array_like A set of atomic forces in displaced supercells. The order of displaced supercells has to match with that in displacement dataset. shape=(displaced supercells, atoms in supercell, 3) """ forces = np.array(forces_fc3, dtype='double', order='C') dataset = self._displacement_dataset if 'first_atoms' in dataset: i = 0 for disp1 in dataset['first_atoms']: disp1['forces'] = forces[i] i += 1 for disp1 in dataset['first_atoms']: for disp2 in disp1['second_atoms']: disp2['forces'] = forces[i] i += 1 elif 'displacements' in dataset or 'forces' in dataset: dataset['forces'] = forces @property def phonon_displacements(self): """Return displacements for harmonic phonons Returns ------- displacements : ndarray Displacements of all atoms of all supercells in Cartesian coordinates. shape=(supercells, natom, 3), dtype='double', order='C' """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset if 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) natom = self._phonon_supercells.get_number_of_atoms() displacements = np.zeros((num_scells, natom, 3), dtype='double', order='C') for i, disp1 in enumerate(dataset['first_atoms']): displacements[i, disp1['number']] = disp1['displacement'] elif 'forces' in dataset or 'displacements' in dataset: displacements = dataset['displacements'] else: raise RuntimeError("displacement dataset has wrong format.") return displacements @phonon_displacements.setter def phonno_displacements(self, displacements): """Set displacemens Parameters ---------- displacemens : array_like Atomic displacements of all atoms of all supercells. Only type2 displacement dataset is supported, i.e., displacements has to have the array shape of (supercells, natom, 3). When displacement dataset is in type1, the type2 dataset is created and force information is lost. """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset disps = np.array(displacements, dtype='double', order='C') natom = self._phonon_supercell.get_number_of_atoms() if (disps.ndim != 3 or disps.shape[1:] != (natom, 3)): raise RuntimeError("Array shape of displacements is incorrect.") if 'first_atoms' in dataset: dataset = {'displacements': disps} elif 'displacements' in dataset or 'forces' in dataset: dataset['displacements'] = disps @property def phonon_forces(self): if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") dataset = self._phonon_displacement_dataset if 'forces' in dataset: return dataset['forces'] elif 'first_atoms' in dataset: num_scells = len(dataset['first_atoms']) forces = np.zeros( (num_scells, self._supercells.get_number_of_atoms(), 3), dtype='double', order='C') for i, disp1 in enumerate(dataset['first_atoms']): forces[i] = disp1['forces'] return forces else: raise RuntimeError("displacement dataset has wrong format.") @phonon_forces.setter def phonon_forces(self, forces_fc2): """Set forces in displacement dataset. Parameters ---------- forces_fc2 : array_like A set of atomic forces in displaced supercells. The order of displaced supercells has to match with that in displacement dataset. shape=(displaced supercells, atoms in supercell, 3) """ if self._phonon_displacement_dataset is None: raise RuntimeError("phonon_displacement_dataset does not exist.") forces = np.array(forces_fc2, dtype='double', order='C') dataset = self._phonon_displacement_dataset if 'first_atoms' in dataset: i = 0 for i, disp1 in enumerate(dataset['first_atoms']): disp1['forces'] = forces[i] i += 1 elif 'displacements' in dataset or 'forces' in dataset: dataset['forces'] = forces @property def dataset(self): return self._displacement_dataset @dataset.setter def dataset(self, dataset): self._displacement_dataset = dataset @property def phonon_dataset(self): return self._phonon_displacement_dataset @phonon_dataset.setter def phonon_dataset(self, dataset): self._phonon_displacement_dataset = dataset @property def band_indices(self): return self._band_indices @band_indices.setter def band_indices(self, band_indices): if band_indices is None: num_band = self._primitive.get_number_of_atoms() * 3 self._band_indices = [np.arange(num_band, dtype='intc')] else: self._band_indices = band_indices self._band_indices_flatten = np.hstack( self._band_indices).astype('intc') def set_band_indices(self, band_indices): self.band_indices = band_indices def set_phph_interaction(self, nac_params=None, nac_q_direction=None, constant_averaged_interaction=None, frequency_scale_factor=None, unit_conversion=None, solve_dynamical_matrices=True): if self._mesh_numbers is None: print("'mesh' has to be set in Phono3py instantiation.") raise RuntimeError self._nac_params = nac_params self._interaction = Interaction( self._supercell, self._primitive, self._mesh_numbers, self._primitive_symmetry, fc3=self._fc3, band_indices=self._band_indices_flatten, constant_averaged_interaction=constant_averaged_interaction, frequency_factor_to_THz=self._frequency_factor_to_THz, frequency_scale_factor=frequency_scale_factor, unit_conversion=unit_conversion, cutoff_frequency=self._cutoff_frequency, is_mesh_symmetry=self._is_mesh_symmetry, symmetrize_fc3q=self._symmetrize_fc3q, lapack_zheev_uplo=self._lapack_zheev_uplo) self._interaction.set_nac_q_direction(nac_q_direction=nac_q_direction) self._interaction.set_dynamical_matrix( self._fc2, self._phonon_supercell, self._phonon_primitive, nac_params=self._nac_params, solve_dynamical_matrices=solve_dynamical_matrices, verbose=self._log_level) def set_phonon_data(self, frequencies, eigenvectors, grid_address): if self._interaction is not None: return self._interaction.set_phonon_data(frequencies, eigenvectors, grid_address) else: return False def get_phonon_data(self): if self._interaction is not None: grid_address = self._interaction.get_grid_address() freqs, eigvecs, _ = self._interaction.get_phonons() return freqs, eigvecs, grid_address else: msg = "set_phph_interaction has to be done." raise RuntimeError(msg) def generate_displacements(self, distance=0.03, cutoff_pair_distance=None, is_plusminus='auto', is_diagonal=True): direction_dataset = get_third_order_displacements( self._supercell, self._symmetry, is_plusminus=is_plusminus, is_diagonal=is_diagonal) self._displacement_dataset = direction_to_displacement( direction_dataset, distance, self._supercell, cutoff_distance=cutoff_pair_distance) if self._phonon_supercell_matrix is not None: # 'is_diagonal=False' below is made intentionally. For # third-order force constants, we need better accuracy, # and I expect this choice is better for it, but not very # sure. # In phono3py, two atoms are displaced for each # configuration and the displacements are chosen, first # displacement from the perfect supercell, then second # displacement, considering symmetry. If I choose # is_diagonal=False for the first displacement, the # symmetry is less broken and the number of second # displacements can be smaller than in the case of # is_diagonal=True for the first displacement. This is # done in the call get_least_displacements() in # phonon3.displacement_fc3.get_third_order_displacements(). # # The call get_least_displacements() is only for the # second order force constants, but 'is_diagonal=False' to # be consistent with the above function call, and also for # the accuracy when calculating ph-ph interaction # strength because displacement directions are better to be # close to perpendicular each other to fit force constants. # # On the discussion of the accuracy, these are just my # expectation when I designed phono3py in the early time, # and in fact now I guess not very different. If these are # little different, then I should not surprise users to # change the default behaviour. At this moment, this is # open question and we will have more advance and should # have better specificy external software on this. phonon_displacement_directions = get_least_displacements( self._phonon_supercell_symmetry, is_plusminus=is_plusminus, is_diagonal=False) self._phonon_displacement_dataset = directions_to_displacement_dataset( phonon_displacement_directions, distance, self._phonon_supercell) def produce_fc2(self, forces_fc2=None, displacement_dataset=None, symmetrize_fc2=False, is_compact_fc=False, fc_calculator=None, fc_calculator_options=None): """Calculate fc2 from displacements and forces Parameters ---------- forces_fc2 : Dummy argument displacement_dataset : dict Displacements in supercells. There are two types of formats. Type 1. Two atomic displacement in each supercell: {'natom': number of atoms in supercell, 'first_atoms': [ {'number': atom index of first displaced atom, 'displacement': displacement in Cartesian coordinates, 'forces': forces on atoms in supercell, 'second_atoms': [ {'number': atom index of second displaced atom, 'displacement': displacement in Cartesian coordinates}, 'forces': forces on atoms in supercell, ... ] }, ... ] } Type 2. All atomic displacements in each supercell: {'displacements': ndarray, dtype='double', order='C', shape=(supercells, atoms in supercell, 3) 'forces': ndarray, dtype='double',, order='C', shape=(supercells, atoms in supercell, 3)} In type 2, displacements and forces can be given by numpy array with different shape but that can be reshaped to (supercells, natom, 3). symmetrize_fc2 : bool Only for type 1 displacement_dataset, translational and permutation symmetries are applied after creating fc3. This symmetrization is not very sophisticated and can break space group symmetry, but often useful. If better symmetrization is expected, it is recommended to use external force constants calculator such as ALM. Default is False. is_compact_fc : bool fc2 shape is False: (supercell, supecell, 3, 3) True: (primitive, supecell, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. fc_calculator : str or None Force constants calculator given by str. fc_calculator_options : dict Options for external force constants calculator. """ if displacement_dataset is None: if self._phonon_displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = self._phonon_displacement_dataset else: disp_dataset = displacement_dataset if forces_fc2 is not None: self.phonon_forces = forces_fc2 msg = ("Forces have to be stored in disp_dataset as written in " "this method's docstring for the type1 dataset.") warnings.warn(msg, DeprecationWarning) if is_compact_fc: p2s_map = self._phonon_primitive.p2s_map else: p2s_map = None if fc_calculator is not None: disps, forces = get_displacements_and_forces(disp_dataset) self._fc2 = get_fc2(self._phonon_supercell, self._phonon_primitive, disps, forces, fc_calculator=fc_calculator, fc_calculator_options=fc_calculator_options, atom_list=p2s_map, log_level=self._log_level) else: self._fc2 = get_phonopy_fc2(self._phonon_supercell, self._phonon_supercell_symmetry, disp_dataset, atom_list=p2s_map) if symmetrize_fc2: if is_compact_fc: symmetrize_compact_force_constants(self._fc2, self._phonon_primitive) else: symmetrize_force_constants(self._fc2) def produce_fc3( self, forces_fc3=None, displacement_dataset=None, cutoff_distance=None, # set fc3 zero symmetrize_fc3r=False, is_compact_fc=False, fc_calculator=None, fc_calculator_options=None): """Calculate fc3 from displacements and forces Parameters ---------- forces_fc3 : Dummy argument displacement_dataset : dict Displacements in supercells. There are two types of formats. Type 1. Two atomic displacement in each supercell: {'natom': number of atoms in supercell, 'first_atoms': [ {'number': atom index of first displaced atom, 'displacement': displacement in Cartesian coordinates, 'forces': forces on atoms in supercell, 'second_atoms': [ {'number': atom index of second displaced atom, 'displacement': displacement in Cartesian coordinates}, 'forces': forces on atoms in supercell, ... ] }, ... ] } Type 2. All atomic displacements in each supercell: {'displacements': ndarray, dtype='double', order='C', shape=(supercells, atoms in supercell, 3) 'forces': ndarray, dtype='double',, order='C', shape=(supercells, atoms in supercell, 3)} In type 2, displacements and forces can be given by numpy array with different shape but that can be reshaped to (supercells, natom, 3). cutoff_distance : float After creating force constants, fc elements where any pair distance in atom triplets larger than cutoff_distance are set zero. symmetrize_fc3r : bool Only for type 1 displacement_dataset, translational and permutation symmetries are applied after creating fc3. This symmetrization is not very sophisticated and can break space group symmetry, but often useful. If better symmetrization is expected, it is recommended to use external force constants calculator such as ALM. Default is False. is_compact_fc : bool fc3 shape is False: (supercell, supercell, supecell, 3, 3, 3) True: (primitive, supercell, supecell, 3, 3, 3) where 'supercell' and 'primitive' indicate number of atoms in these cells. Default is False. fc_calculator : str or None Force constants calculator given by str. fc_calculator_options : dict Options for external force constants calculator. """ if displacement_dataset is None: disp_dataset = self._displacement_dataset else: disp_dataset = displacement_dataset if forces_fc3 is not None: self.forces = forces_fc3 msg = ("Forces have to be stored in disp_dataset as written in " "this method's docstring for the type1 dataset.") warnings.warn(msg, DeprecationWarning) if fc_calculator is not None: from phono3py.other.fc_calculator import ( get_fc3, get_displacements_and_forces_fc3) disps, forces = get_displacements_and_forces_fc3(disp_dataset) fc2, fc3 = get_fc3(self._supercell, self._primitive, disps, forces, fc_calculator=fc_calculator, fc_calculator_options=fc_calculator_options, is_compact_fc=is_compact_fc, log_level=self._log_level) else: fc2, fc3 = get_phono3py_fc3(self._supercell, self._primitive, disp_dataset, self._symmetry, is_compact_fc=is_compact_fc, verbose=self._log_level) if symmetrize_fc3r: if is_compact_fc: set_translational_invariance_compact_fc3( fc3, self._primitive) set_permutation_symmetry_compact_fc3(fc3, self._primitive) if self._fc2 is None: symmetrize_compact_force_constants( fc2, self._primitive) else: set_translational_invariance_fc3(fc3) set_permutation_symmetry_fc3(fc3) if self._fc2 is None: symmetrize_force_constants(fc2) # Set fc2 and fc3 self._fc3 = fc3 # Normally self._fc2 is overwritten in produce_fc2 if self._fc2 is None: self._fc2 = fc2 def cutoff_fc3_by_zero(self, cutoff_distance, fc3=None): if fc3 is None: _fc3 = self._fc3 else: _fc3 = fc3 cutoff_fc3_by_zero( _fc3, # overwritten self._supercell, cutoff_distance, self._symprec) def set_permutation_symmetry(self): if self._fc2 is not None: set_permutation_symmetry(self._fc2) if self._fc3 is not None: set_permutation_symmetry_fc3(self._fc3) def set_translational_invariance(self): if self._fc2 is not None: set_translational_invariance(self._fc2) if self._fc3 is not None: set_translational_invariance_fc3(self._fc3) @property def version(self): return __version__ def get_version(self): return self.version def get_interaction_strength(self): return self._interaction @property def fc2(self): return self._fc2 def get_fc2(self): return self.fc2 @fc2.setter def fc2(self, fc2): self._fc2 = fc2 def set_fc2(self, fc2): self.fc2 = fc2 @property def fc3(self): return self._fc3 def get_fc3(self): return self.fc3 @fc3.setter def fc3(self, fc3): self._fc3 = fc3 def set_fc3(self, fc3): self.fc3 = fc3 @property def nac_params(self): return self._nac_params def get_nac_params(self): return self.nac_params @property def primitive(self): return self._primitive def get_primitive(self): return self.primitive @property def unitcell(self): return self._unitcell def get_unitcell(self): return self.unitcell @property def supercell(self): return self._supercell def get_supercell(self): return self.supercell @property def phonon_supercell(self): return self._phonon_supercell def get_phonon_supercell(self): return self.phonon_supercell @property def phonon_primitive(self): return self._phonon_primitive def get_phonon_primitive(self): return self.phonon_primitive @property def symmetry(self): """return symmetry of supercell""" return self._symmetry def get_symmetry(self): return self.symmetry @property def primitive_symmetry(self): """return symmetry of primitive cell""" return self._primitive_symmetry def get_primitive_symmetry(self): """return symmetry of primitive cell""" return self.primitive_symmetry @property def phonon_supercell_symmetry(self): return self._phonon_supercell_symmetry def get_phonon_supercell_symmetry(self): return self.phonon_supercell_symmetry @property def supercell_matrix(self): return self._supercell_matrix def get_supercell_matrix(self): return self.supercell_matrix @property def phonon_supercell_matrix(self): return self._phonon_supercell_matrix def get_phonon_supercell_matrix(self): return self.phonon_supercell_matrix @property def primitive_matrix(self): return self._primitive_matrix def get_primitive_matrix(self): return self.primitive_matrix @property def unit_conversion_factor(self): return self._frequency_factor_to_THz def set_displacement_dataset(self, dataset): self._displacement_dataset = dataset @property def dataset(self): return self._displacement_dataset @property def displacement_dataset(self): return self.dataset def get_displacement_dataset(self): return self.displacement_dataset @property def phonon_displacement_dataset(self): return self._phonon_displacement_dataset def get_phonon_displacement_dataset(self): return self.phonon_displacement_dataset @property def supercells_with_displacements(self): if self._supercells_with_displacements is None: self._build_supercells_with_displacements() return self._supercells_with_displacements def get_supercells_with_displacements(self): return self.supercells_with_displacements @property def phonon_supercells_with_displacements(self): if self._phonon_supercells_with_displacements is None: if self._phonon_displacement_dataset is not None: self._phonon_supercells_with_displacements = \ self._build_phonon_supercells_with_displacements( self._phonon_supercell, self._phonon_displacement_dataset) return self._phonon_supercells_with_displacements def get_phonon_supercells_with_displacements(self): return self.phonon_supercells_with_displacements @property def mesh_numbers(self): return self._mesh_numbers def run_imag_self_energy(self, grid_points, frequency_step=None, num_frequency_points=None, temperatures=None, scattering_event_class=None, write_gamma_detail=False, output_filename=None): if self._interaction is None: self.set_phph_interaction() if temperatures is None: temperatures = [0.0, 300.0] self._grid_points = grid_points self._temperatures = temperatures self._scattering_event_class = scattering_event_class self._imag_self_energy, self._frequency_points = get_imag_self_energy( self._interaction, grid_points, self._sigmas, frequency_step=frequency_step, num_frequency_points=num_frequency_points, temperatures=temperatures, scattering_event_class=scattering_event_class, write_detail=write_gamma_detail, output_filename=output_filename, log_level=self._log_level) def write_imag_self_energy(self, filename=None): write_imag_self_energy( self._imag_self_energy, self._mesh_numbers, self._grid_points, self._band_indices, self._frequency_points, self._temperatures, self._sigmas, scattering_event_class=self._scattering_event_class, filename=filename, is_mesh_symmetry=self._is_mesh_symmetry) def run_thermal_conductivity( self, is_LBTE=False, temperatures=np.arange(0, 1001, 10, dtype='double'), is_isotope=False, mass_variances=None, grid_points=None, boundary_mfp=None, # in micrometre solve_collective_phonon=False, use_ave_pp=False, gamma_unit_conversion=None, mesh_divisors=None, coarse_mesh_shifts=None, is_reducible_collision_matrix=False, is_kappa_star=True, gv_delta_q=None, # for group velocity is_full_pp=False, pinv_cutoff=1.0e-8, # for pseudo-inversion of collision matrix pinv_solver=0, # solver of pseudo-inversion of collision matrix write_gamma=False, read_gamma=False, is_N_U=False, write_kappa=False, write_gamma_detail=False, write_collision=False, read_collision=False, write_pp=False, read_pp=False, write_LBTE_solution=False, compression=None, input_filename=None, output_filename=None): if self._interaction is None: self.set_phph_interaction() if is_LBTE: self._thermal_conductivity = get_thermal_conductivity_LBTE( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, solve_collective_phonon=solve_collective_phonon, is_reducible_collision_matrix=is_reducible_collision_matrix, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, pinv_cutoff=pinv_cutoff, pinv_solver=pinv_solver, write_collision=write_collision, read_collision=read_collision, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_LBTE_solution=write_LBTE_solution, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) else: self._thermal_conductivity = get_thermal_conductivity_RTA( self._interaction, self._primitive_symmetry, temperatures=temperatures, sigmas=self._sigmas, sigma_cutoff=self._sigma_cutoff, is_isotope=is_isotope, mass_variances=mass_variances, grid_points=grid_points, boundary_mfp=boundary_mfp, use_ave_pp=use_ave_pp, gamma_unit_conversion=gamma_unit_conversion, mesh_divisors=mesh_divisors, coarse_mesh_shifts=coarse_mesh_shifts, is_kappa_star=is_kappa_star, gv_delta_q=gv_delta_q, is_full_pp=is_full_pp, write_gamma=write_gamma, read_gamma=read_gamma, is_N_U=is_N_U, write_kappa=write_kappa, write_pp=write_pp, read_pp=read_pp, write_gamma_detail=write_gamma_detail, compression=compression, input_filename=input_filename, output_filename=output_filename, log_level=self._log_level) def get_frequency_shift(self, grid_points, temperatures=np.arange(0, 1001, 10, dtype='double'), epsilons=None, output_filename=None): """Frequency shift from lowest order diagram is calculated. Args: epslins(list of float): The value to avoid divergence. When multiple values are given frequency shifts for those values are returned. """ if self._interaction is None: self.set_phph_interaction() if epsilons is None: _epsilons = [0.1] else: _epsilons = epsilons self._grid_points = grid_points get_frequency_shift(self._interaction, self._grid_points, self._band_indices, _epsilons, temperatures, output_filename=output_filename, log_level=self._log_level) def _search_symmetry(self): self._symmetry = Symmetry(self._supercell, self._symprec, self._is_symmetry) def _search_primitive_symmetry(self): self._primitive_symmetry = Symmetry(self._primitive, self._symprec, self._is_symmetry) if (len(self._symmetry.get_pointgroup_operations()) != len( self._primitive_symmetry.get_pointgroup_operations())): print("Warning: point group symmetries of supercell and primitive" "cell are different.") def _search_phonon_supercell_symmetry(self): if self._phonon_supercell_matrix is None: self._phonon_supercell_symmetry = self._symmetry else: self._phonon_supercell_symmetry = Symmetry(self._phonon_supercell, self._symprec, self._is_symmetry) def _build_supercell(self): self._supercell = get_supercell(self._unitcell, self._supercell_matrix, self._symprec) def _build_primitive_cell(self): """ primitive_matrix: Relative axes of primitive cell to the input unit cell. Relative axes to the supercell is calculated by: supercell_matrix^-1 * primitive_matrix Therefore primitive cell lattice is finally calculated by: (supercell_lattice * (supercell_matrix)^-1 * primitive_matrix)^T """ self._primitive = self._get_primitive_cell(self._supercell, self._supercell_matrix, self._primitive_matrix) def _build_phonon_supercell(self): """ phonon_supercell: This supercell is used for harmonic phonons (frequencies, eigenvectors, group velocities, ...) phonon_supercell_matrix: Different supercell size can be specified. """ if self._phonon_supercell_matrix is None: self._phonon_supercell = self._supercell else: self._phonon_supercell = get_supercell( self._unitcell, self._phonon_supercell_matrix, self._symprec) def _build_phonon_primitive_cell(self): if self._phonon_supercell_matrix is None: self._phonon_primitive = self._primitive else: self._phonon_primitive = self._get_primitive_cell( self._phonon_supercell, self._phonon_supercell_matrix, self._primitive_matrix) if (self._primitive is not None and (self._primitive.get_atomic_numbers() != self._phonon_primitive.get_atomic_numbers()).any()): print(" Primitive cells for fc2 and fc3 can be different.") raise RuntimeError def _build_phonon_supercells_with_displacements(self, supercell, displacement_dataset): supercells = [] magmoms = supercell.get_magnetic_moments() masses = supercell.get_masses() numbers = supercell.get_atomic_numbers() lattice = supercell.get_cell() for disp1 in displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] positions = supercell.get_positions() positions[disp1['number']] += disp_cart1 supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) return supercells def _build_supercells_with_displacements(self): supercells = [] magmoms = self._supercell.get_magnetic_moments() masses = self._supercell.get_masses() numbers = self._supercell.get_atomic_numbers() lattice = self._supercell.get_cell() supercells = self._build_phonon_supercells_with_displacements( self._supercell, self._displacement_dataset) for disp1 in self._displacement_dataset['first_atoms']: disp_cart1 = disp1['displacement'] for disp2 in disp1['second_atoms']: if 'included' in disp2: included = disp2['included'] else: included = True if included: positions = self._supercell.get_positions() positions[disp1['number']] += disp_cart1 positions[disp2['number']] += disp2['displacement'] supercells.append( Atoms(numbers=numbers, masses=masses, magmoms=magmoms, positions=positions, cell=lattice, pbc=True)) else: supercells.append(None) self._supercells_with_displacements = supercells def _get_primitive_cell(self, supercell, supercell_matrix, primitive_matrix): inv_supercell_matrix = np.linalg.inv(supercell_matrix) if primitive_matrix is None: t_mat = inv_supercell_matrix else: t_mat = np.dot(inv_supercell_matrix, primitive_matrix) return get_primitive(supercell, t_mat, self._symprec) def _guess_primitive_matrix(self): return guess_primitive_matrix(self._unitcell, symprec=self._symprec) def _set_masses(self, masses): p_masses = np.array(masses) self._primitive.set_masses(p_masses) p2p_map = self._primitive.get_primitive_to_primitive_map() s_masses = p_masses[[ p2p_map[x] for x in self._primitive.get_supercell_to_primitive_map() ]] self._supercell.set_masses(s_masses) u2s_map = self._supercell.get_unitcell_to_supercell_map() u_masses = s_masses[u2s_map] self._unitcell.set_masses(u_masses) self._phonon_primitive.set_masses(p_masses) p2p_map = self._phonon_primitive.get_primitive_to_primitive_map() s_masses = p_masses[[ p2p_map[x] for x in self._phonon_primitive.get_supercell_to_primitive_map() ]] self._phonon_supercell.set_masses(s_masses) def _set_mesh_numbers(self, mesh): _mesh = np.array(mesh) mesh_nums = None if _mesh.shape: if _mesh.shape == (3, ): mesh_nums = mesh elif self._primitive_symmetry is None: mesh_nums = length2mesh(mesh, self._primitive.get_cell()) else: rotations = self._primitive_symmetry.get_pointgroup_operations() mesh_nums = length2mesh(mesh, self._primitive.get_cell(), rotations=rotations) if mesh_nums is None: msg = "mesh has inappropriate type." raise TypeError(msg) self._mesh_numbers = mesh_nums
class Interaction: def __init__(self, fc3, supercell, primitive, mesh, band_indices=None, frequency_factor_to_THz=VaspToTHz, is_nosym=False, symmetrize_fc3_q=False, symprec=1e-3, triplet_cut_super = None, triplet_cut_prim = None, cutoff_frequency=None, cutoff_hfrequency=None, cutoff_delta = None, is_triplets_dispersed=False, is_read_amplitude=False, is_write_amplitude = False, is_triplets_permute=True, lapack_zheev_uplo='L'): self._fc3 = fc3 self._supercell = supercell self._primitive = primitive self._mesh = np.intc(mesh) num_band = primitive.get_number_of_atoms() * 3 if band_indices is None: self._band_indices = np.arange(num_band, dtype='intc') else: self._band_indices = band_indices.astype("intc") self._frequency_factor_to_THz = frequency_factor_to_THz self._symprec = symprec self._is_tripelts_permute=is_triplets_permute natom_super = supercell.get_number_of_atoms() natom_prim = primitive.get_number_of_atoms() if triplet_cut_super is None: self._triplet_cut_super=np.zeros((natom_super, natom_super, natom_super), dtype='bool') else: self._triplet_cut_super=triplet_cut_super if triplet_cut_prim is None: self._triplet_cut_prim=np.zeros((natom_prim, natom_prim, natom_prim), dtype='bool') else: self._triplet_cut_prim=triplet_cut_prim if cutoff_delta is None: self._cutoff_delta = 1000.0 else: self._cutoff_delta = cutoff_delta if cutoff_frequency is None: self._cutoff_frequency = 0 else: self._cutoff_frequency = cutoff_frequency if cutoff_hfrequency is None: self._cutoff_hfrequency = 10000.0 # THz elif symmetrize_fc3_q: print "Warning: symmetryze_fc3_q and cutoff_hfrequency are not compatible" self._cutoff_hfrequency = 10000.0 else: self._cutoff_hfrequency = cutoff_hfrequency self._is_nosym = is_nosym self._symmetrize_fc3_q = symmetrize_fc3_q self._lapack_zheev_uplo = lapack_zheev_uplo self._symmetry = Symmetry(primitive, symprec=symprec) if self._is_nosym: self._point_group_operations = np.array([np.eye(3)],dtype="intc") self._kpoint_operations = get_kpoint_group(self._mesh, self._point_group_operations, is_time_reversal=False) else: self._point_group_operations = self._symmetry.get_pointgroup_operations() self._kpoint_operations = get_kpoint_group(self._mesh, self._point_group_operations) if self.is_nosym(): grid_mapping = np.arange(np.prod(self._mesh)) grid_mapping_rots = np.zeros(len(grid_mapping), dtype="intc") else: grid_mapping, grid_mapping_rots = get_mappings(self._mesh, self.get_point_group_operations(), qpoints=np.array([0,0,0],dtype="double")) self._svecs, self._multiplicity = get_smallest_vectors(self._supercell, self._primitive, self._symprec) self._grid_mapping = grid_mapping self._grid_mapping_rot = grid_mapping_rots self._is_read_amplitude = is_read_amplitude self._is_write_amplitude = is_write_amplitude self._grid_point = None self._triplets_at_q = None self._weights_at_q = None self._grid_address = None self._interaction_strength = None self._phonon_done = None self._frequencies = None self._eigenvectors = None self._degenerates = None self._grid_points = None self._dm = None self._nac_q_direction = None self._triplets = None self._triplets_mappings = None self._triplets_sequence = None self._unique_triplets = None self._amplitude_all = None self._is_dispersed = is_triplets_dispersed self._allocate_phonon() def set_is_write_amplitude(self, is_write_amplitude=False): self._is_write_amplitude = is_write_amplitude def get_is_write_amplitude(self): return self._is_write_amplitude def set_is_read_amplitude(self, is_read_amplitude=False): self._is_read_amplitude = is_read_amplitude def get_is_read_amplitude(self): return self._is_read_amplitude def get_is_dispersed(self): return self._is_dispersed def get_degeneracy(self): return self._degenerates def get_amplitude_all(self): return self._amplitude_all def set_is_disperse(self, is_disperse): self._is_dispersed = is_disperse def set_is_symmetrize_fc3_q(self, is_symmetrize_fc3q): self._symmetrize_fc3_q = is_symmetrize_fc3q def get_is_symmetrize_fc3_q(self): return self._symmetrize_fc3_q def run(self, g_skip=None, lang='C', log_level=0): num_band = self._primitive.get_number_of_atoms() * 3 num_triplets = len(self._triplets_at_q) self._interaction_strength = np.zeros( (num_triplets, len(self._band_indices), num_band, num_band), dtype='double') if self._is_dispersed: unitrip_indices = self._triplets_uniq_index_at_grid mapping = self._triplets_maping_at_grid triplets_sequence = self._triplet_sequence_at_grid if log_level>0: print "Triplets number to be calculated: %d/%d" %(len(unitrip_indices), len(self._interaction_strength)) if self._is_read_amplitude: self._interaction_strength_reduced = \ read_amplitude_from_hdf5_at_grid(self._mesh, self._grid_point) if self._interaction_strength_reduced is not None: self.set_phonons(lang=lang) else: print "Reading amplitude in the disperse mode unsuccessfully. Reverting to writing mode!" self._is_read_amplitude = False self._is_write_amplitude = True if not self._is_read_amplitude: self._interaction_strength_reduced = np.zeros( (len(self._triplets_uniq_index_at_grid), len(self._band_indices), num_band, num_band), dtype='double') self._triplets_at_q_reduced = self._triplets_at_q[unitrip_indices].copy() if g_skip is None: g_skip = np.zeros_like(self._interaction_strength_reduced, dtype="bool") if lang == 'C': self._run_c(g_skip=g_skip) else: self._run_py(g_skip=g_skip) import anharmonic._phono3py as phono3c if len(self._band_indices) != self._interaction_strength.shape[-1]: assert (triplets_sequence[:,0] == 0).all() phono3c.interaction_from_reduced(self._interaction_strength, self._interaction_strength_reduced.astype("double").copy(), mapping.astype("intc"), triplets_sequence.astype("byte")) #debugging # interaction_strength = self._interaction_strength.copy() # interaction_strength_reduced = self._interaction_strength_reduced.copy() # self._interaction_strength_reduced = np.zeros( # (len(self._triplets_at_q), len(self._band_indices), num_band, num_band), # dtype='double') # self._triplets_at_q_reduced = self._triplets_at_q # self._run_c() # self._interaction_strength = self._interaction_strength_reduced.copy() # print np.abs(self._interaction_strength_reduced - interaction_strength).max() / self._interaction_strength_reduced.max(), # print np.unravel_index(np.abs(self._interaction_strength_reduced - interaction_strength).argmax(), self._interaction_strength_reduced.shape) if self._is_write_amplitude: write_amplitude_to_hdf5_at_grid(self._mesh, grid=self._grid_point, amplitude=self._interaction_strength_reduced) else: if self._is_read_amplitude: import anharmonic._phono3py as phono3c phono3c.interaction_from_reduced(self._interaction_strength, self._amplitude_all, self._triplets_mappings[self._i].astype("intc"), self._triplets_sequence[self._i].astype("byte")) self.set_phonons(lang=lang) else: map_to_unique, reverse_map = np.unique(self._triplets_mappings[self._i], return_inverse=True) undone_num = np.extract(self._triplets_done[map_to_unique]==0, map_to_unique) self._triplets_at_q_reduced = self._unique_triplets[undone_num] if log_level>0: print "Triplets number to be calculted: %d/%d" %(len(self._triplets_at_q_reduced), len(self._interaction_strength)) self._interaction_strength_reduced = np.zeros( (len(self._triplets_at_q_reduced), len(self._band_indices), num_band, num_band), dtype='double') if g_skip is None: g_skip = np.zeros_like(self._interaction_strength_reduced, dtype="bool") if lang == 'C': self._run_c(g_skip=g_skip) else: self._run_py(g_skip=g_skip) self._amplitude_all[undone_num] = self._interaction_strength_reduced[:] import anharmonic._phono3py as phono3c phono3c.interaction_from_reduced(self._interaction_strength, self._amplitude_all, self._triplets_mappings[self._i].astype("intc"), self._triplets_sequence[self._i].astype("byte")) self._triplets_done[undone_num] = True ##Added for my own purpose, please delete it self._criteria = 0.55 self._lcriteria = 0.1 is_anyq_on_bound = (np.abs(self._grid_address[self._triplets_at_q]) > self._criteria * self._mesh).any(axis=(1,2)) is_uprocess = np.any(self._grid_address[self._triplets_at_q].sum(axis=1)!=0, axis=1) is_anyq_at_center = (np.abs(self._grid_address[self._triplets_at_q]) < self._lcriteria * self._mesh).any(axis=(1,2)) self._interaction_strength[np.where(np.logical_and(is_uprocess, is_anyq_at_center))] = 0. self._interaction_strength[np.where(is_anyq_on_bound)] = 0. # @total_time.timeit def set_phonons(self, grid_points=None, lang = "C"): if lang == "C": self._set_phonon_c(grid_points) else: if grid_points is None: for i, grid_triplet in enumerate(self._triplets_at_q): for gp in grid_triplet: self._set_phonon_py(gp) else: for gp in enumerate(grid_points): self._set_phonon_py(gp) def set_phonons_all(self, is_band_connection=True, lang='C', log_level=1): if log_level: print "calculate phonon frequencies of all phonon mode..." grid_points = np.arange(len(self._grid_address)) if lang == "C": self._set_phonon_c(grid_points) else: for gp in grid_points: self._set_phonon_py(gp) if is_band_connection: self._set_band_connection() def _set_band_connection(self): nqpoint, nband = self._frequencies.shape is_non_degenerate = np.all(self._degenerates == np.arange(nband), axis=1) connect_done = np.zeros(nqpoint, dtype='bool') start = np.where(is_non_degenerate)[0][0] while True: connect_done[start] = True neighbors = np.argsort(np.sum(np.abs(self._grid_address - self._grid_address[start]), axis=1)) undone = np.extract(connect_done[neighbors]==False, neighbors) if not (len(undone) == 0 or (is_non_degenerate[undone] == False).all()): new = np.extract(is_non_degenerate[undone], undone)[0] new_neighbors = np.argsort(np.sum(np.abs(self._grid_address - self._grid_address[new]), axis=1)) new_neighbor_done = np.extract(connect_done[new_neighbors], new_neighbors)[0] bo = estimate_band_connection(self._eigenvectors[new_neighbor_done], self._eigenvectors[new], np.arange(nband)) if bo is not None: self._frequencies[new] = (self._frequencies[new])[bo] self._eigenvectors[new] = (self._eigenvectors[new].T)[bo].T # Since all the phonons here are non-degenerate, the step is missed start = new else: break for new in undone: new_neighbors = np.argsort(np.sum(np.abs(self._grid_address - self._grid_address[new]), axis=1)) new_neighbor_done = np.extract(connect_done[new_neighbors], new_neighbors)[0] deg = [np.where(self._degenerates[new] == j)[0] for j in np.unique(self._degenerates[new])] bo = estimate_band_connection(self._eigenvectors[new_neighbor_done], self._eigenvectors[new], np.arange(nband), degenerate_sets=deg) if bo is not None: self._frequencies[new] = (self._frequencies[new])[bo] self._eigenvectors[new] = (self._eigenvectors[new].T)[bo].T self._degenerates[new] = (self._degenerates[new])[bo].copy() def get_interaction_strength(self): return self._interaction_strength def get_mesh_numbers(self): return self._mesh def get_phonons(self): return (self._frequencies, self._eigenvectors, self._phonon_done) def get_dynamical_matrix(self): return self._dm def get_primitive(self): return self._primitive def get_supercell(self): return self._supercell def get_point_group_operations(self): return self._point_group_operations def get_triplets_at_q(self): return self._triplets_at_q, self._weights_at_q def get_triplets_done(self): return self._triplets_done def get_triplets_sequence_at_q_disperse(self): return self._triplet_sequence_at_grid def get_triplets_mapping_at_q_disperse(self): return self._triplets_maping_at_grid def get_triplets_at_q_nu(self): triplet_address = self.get_triplet_address() triplets_N=[] triplets_U=[] for i, t in enumerate(triplet_address): if (np.sum(t, axis=0)==0).all(): triplets_N.append(i) else: triplets_U.append(i) return (triplets_N, triplets_U) def get_triplet_address(self): return self._triplets_address def get_grid_address(self): return self._grid_address def get_band_indices(self): return self._band_indices def get_frequency_factor_to_THz(self): return self._frequency_factor_to_THz def get_lapack_zheev_uplo(self): return self._lapack_zheev_uplo def is_nosym(self): return self._is_nosym def get_bz_map(self): return self._bz_map def get_bz_to_pp_map(self): return self._bz_to_pp_map def get_grid_mapping(self): return self._grid_mapping def get_grid_mapping_rot(self): return self._grid_mapping_rot def get_cutoff_frequency(self): return self._cutoff_frequency def get_cutoff_hfrequency(self): return self._cutoff_hfrequency def get_triplets_mapping_at_grid(self): if not self._is_dispersed: return self._triplets_mappings[self._i] else: return self._triplets_maping_at_grid def get_triplets_sequence_at_grid(self): return self._triplets_sequence[self._i] @total_time.timeit def set_grid_point(self, grid_point, i=None, stores_triplets_map=False): if i==None: self._grid_point = grid_point if self._grid_points is not None: self._i = np.where(grid_point == self._grid_points)[0][0] else: self._i = i self._grid_point = self._grid_points[i] if self._triplets is not None: self._triplets_at_q = self._triplets[self._i] self._weights_at_q = self._weights[self._i] self._triplets_address = self._grid_address[self._triplets_at_q] if self._is_dispersed: self._triplets_uniq_index_at_grid = self._unique_triplets[self._i] self._triplets_maping_at_grid = self._triplets_mappings[self._i] self._triplet_sequence_at_grid = self._triplets_sequence[self._i] else: reciprocal_lattice = np.linalg.inv(self._primitive.get_cell()) if self._is_nosym: (triplets_at_q, weights_at_q, grid_address, bz_map, triplets_map_at_q, ir_map_at_q) = get_nosym_triplets_at_q( grid_point, self._mesh, reciprocal_lattice, stores_triplets_map=stores_triplets_map) if self._is_dispersed: self._triplets_uniq_index_at_grid = np.arange(len(triplets_at_q), dtype="intc") self._triplets_maping_at_grid = np.arange(len(triplets_at_q), dtype="intc") self._triplet_sequence_at_grid = np.arange(3, dtype="intc")[np.newaxis].repeat(len(triplets_at_q)) else: (triples_at_q_crude, weights_at_q, grid_address, grid_map)=\ get_triplets_at_q_crude(grid_point, self._mesh, self._point_group_operations) (triplets_at_q, weights, bz_grid_address, bz_map)=\ get_BZ_triplets_at_q(grid_point, self._mesh, reciprocal_lattice, grid_address, grid_map) if self._is_dispersed: (unique_triplet_nums, triplets_mappings, triplet_sequence) = \ reduce_triplets_by_permutation_symmetry([triples_at_q_crude], self._mesh, first_mapping=self._grid_mapping, first_rotation=self._kpoint_operations[self._grid_mapping_rot], second_mapping=np.array([grid_map])) self._triplets_uniq_index_at_grid = unique_triplet_nums self._triplets_maping_at_grid = triplets_mappings[0] self._triplet_sequence_at_grid = triplet_sequence[0] sum_qs = bz_grid_address[triplets_at_q].sum(axis=1) resi = sum_qs % self._mesh if (resi != 0).any(): triplets = triplets_at_q[np.where(resi != 0)[0]] print "============= Warning ==================" print triplets print sum_qs print "============= Warning ==================" self._triplets_at_q = triplets_at_q self._weights_at_q = weights_at_q self._ir_map_at_q = grid_map def set_grid_points(self, grid_points): self._grid_points = grid_points reciprocal_lattice = np.linalg.inv(self._primitive.get_cell()) self._triplets = [] self._weights = [] second_mappings = [] self._triplets_sequence = [] crude_triplets = [] total_triplet_num = 0 # unique triplets at all grid points self._unique_triplets = [] self._triplets_mappings = [] self._triplets_sequence = [] for g, grid_point in enumerate(grid_points): (triples_at_q_crude, weights_at_q, grid_address, grid_map)=\ get_triplets_at_q_crude(grid_point, self._mesh, self._point_group_operations) (triplets_at_q, weights, bz_grid_address, bz_map)=\ get_BZ_triplets_at_q(grid_point, self._mesh, reciprocal_lattice, grid_address, grid_map) crude_triplets.append(triples_at_q_crude) self._triplets.append(triplets_at_q) self._weights.append(weights_at_q) total_triplet_num += len(triplets_at_q) second_mappings.append(grid_map) if self._is_dispersed: (unique_triplet_nums_grid, triplets_mappings_grid, triplet_sequence_grid) = \ reduce_triplets_by_permutation_symmetry([triples_at_q_crude], self._mesh, first_mapping=self._grid_mapping, first_rotation=self._kpoint_operations[self._grid_mapping_rot], second_mapping=np.array([grid_map])) self._unique_triplets.append(unique_triplet_nums_grid) self._triplets_mappings.append(triplets_mappings_grid[0]) self._triplets_sequence.append(triplet_sequence_grid[0]) if not self._is_dispersed: unique_triplet_num, triplets_mappings, triplet_sequence = reduce_triplets_by_permutation_symmetry(crude_triplets, self._mesh, first_mapping=self._grid_mapping, first_rotation=self._kpoint_operations[self._grid_mapping_rot], second_mapping=np.vstack(second_mappings)) print "Number of total unique triplets after permutation symmetry: %d/ %d" %(len(unique_triplet_num), total_triplet_num) self._unique_triplets = np.vstack(self._triplets)[unique_triplet_num] self._triplets_done = np.zeros(len(unique_triplet_num), dtype="byte") self._triplets_mappings = triplets_mappings #map to the index of the triplets in the unique_triplet_num self._second_mappings = second_mappings self._triplets_sequence = triplet_sequence nband = 3 * self._primitive.get_number_of_atoms() try: self._amplitude_all = np.zeros((len(self._unique_triplets), nband, nband, nband), dtype="double") except MemoryError: print "A memory error occurs in allocating the whole interaction strength array" print "--disperse is recommended as an alternative method" sys.exit(1) if self._is_read_amplitude: self.read_amplitude_all() def read_amplitude_all(self): if not self._is_dispersed: read_amplitude_from_hdf5_all(self._amplitude_all, self._mesh, self.is_nosym()) def release_amplitude_all(self): del self._amplitude_all self._amplitude_all = None def set_dynamical_matrix(self, fc2, supercell, primitive, nac_params=None, frequency_scale_factor=None, decimals=None): self._dm = get_dynamical_matrix( fc2, supercell, primitive, nac_params=nac_params, frequency_scale_factor=frequency_scale_factor, decimals=decimals, symprec=self._symprec) def set_nac_q_direction(self, nac_q_direction=None): if nac_q_direction is not None: self._nac_q_direction = np.double(nac_q_direction) def _run_c(self, g_skip=None): import anharmonic._phono3py as phono3c if g_skip is None: g_skip = np.zeros_like(self._interaction_strength_reduced, dtype="bool") assert g_skip.shape == self._interaction_strength_reduced.shape self._set_phonon_c() masses = np.double(self._primitive.get_masses()) p2s = np.intc(self._primitive.get_primitive_to_supercell_map()) s2p = np.intc(self._primitive.get_supercell_to_primitive_map()) atc=np.intc(self._triplet_cut_super) # int type atc_prim = np.intc(self._triplet_cut_prim) # int type phono3c.interaction(self._interaction_strength_reduced, self._frequencies, self._eigenvectors, self._triplets_at_q_reduced.copy(), self._grid_address, self._mesh, self._fc3, atc, atc_prim, g_skip, self._svecs, self._multiplicity, np.double(masses), p2s, s2p, self._band_indices, self._symmetrize_fc3_q, self._cutoff_frequency, self._cutoff_hfrequency, self._cutoff_delta) phono3c.interaction_degeneracy_grid(self._interaction_strength_reduced, self._degenerates, self._triplets_at_q_reduced.astype('intc').copy(), self._band_indices.astype('intc')) # from itertools import permutations # interaction = np.zeros((6,) + self._interaction_strength_reduced.shape, dtype='double') # for i, permute in enumerate(permutations((0,1,2))): # interaction0 = np.zeros_like(self._interaction_strength_reduced) # new = ''.join(np.array(list('ijk'))[list(permute)]) # phono3c.interaction(interaction0, # self._frequencies, # self._eigenvectors, # self._triplets_at_q_reduced[:, permute].copy(), # self._grid_address, # self._mesh, # self._fc3, # atc, # np.zeros_like(g_skip), # svecs, # multiplicity, # np.double(masses), # p2s, # s2p, # self._band_indices, # False, # self._cutoff_frequency, # self._cutoff_hfrequency, # self._cutoff_delta) # interaction[i,:] = np.einsum("N%s->Nijk"%new, interaction0) # diff = np.abs(interaction - interaction[0]) # print np.unravel_index(diff.argmax(), diff.shape), diff.max() def _set_phonon_c(self, grid_points=None): import anharmonic._phono3py as phono3c svecs, multiplicity = self._dm.get_shortest_vectors() masses = np.double(self._dm.get_primitive().get_masses()) rec_lattice = np.double( np.linalg.inv(self._dm.get_primitive().get_cell())).copy() if self._dm.is_nac(): born = self._dm.get_born_effective_charges() nac_factor = self._dm.get_nac_factor() dielectric = self._dm.get_dielectric_constant() else: born = None nac_factor = 0 dielectric = None if grid_points == None: phono3c.phonon_triplets(self._frequencies, self._eigenvectors, self._degenerates, self._phonon_done, self._triplets_at_q, self._grid_address, self._mesh, self._dm.get_force_constants(), svecs, multiplicity, masses, self._dm.get_primitive_to_supercell_map(), self._dm.get_supercell_to_primitive_map(), self._frequency_factor_to_THz, born, dielectric, rec_lattice, self._nac_q_direction, nac_factor, self._lapack_zheev_uplo) else: set_phonon_c(self._dm, self._frequencies, self._eigenvectors, self._degenerates, self._phonon_done, grid_points.astype("intc").copy(), self._grid_address, self._mesh, self._frequency_factor_to_THz, self._nac_q_direction, self._lapack_zheev_uplo) def _run_py(self, g_skip=None): if g_skip is None: g_skip = np.zeros_like(self._interaction_strength_reduced, dtype="bool") else: assert g_skip.shape == self._interaction_strength_reduced.shape r2r = RealToReciprocal(self._fc3, self._supercell, self._primitive, self._mesh, symprec=self._symprec, atom_triplet_cut=self._triplet_cut_super) r2n = ReciprocalToNormal(self._primitive, self._frequencies, self._eigenvectors, cutoff_frequency=self._cutoff_frequency, cutoff_hfrequency=self._cutoff_hfrequency, cutoff_delta=self._cutoff_delta) for i, grid_triplet in enumerate(self._triplets_at_q_reduced): print "%d / %d" % (i + 1, len(self._triplets_at_q_reduced)) r2r.run(self._grid_address[grid_triplet], self._symmetrize_fc3_q) fc3_reciprocal = r2r.get_fc3_reciprocal() for gp in grid_triplet: self._set_phonon_py(gp) r2n.run(fc3_reciprocal, grid_triplet, g_skip = g_skip[i]) self._interaction_strength_reduced[i] = r2n.get_reciprocal_to_normal() def _set_phonon_py(self, grid_point): set_phonon_py(grid_point, self._phonon_done, self._frequencies, self._eigenvectors, self._degenerates, self._grid_address, self._mesh, self._dm, self._frequency_factor_to_THz, self._lapack_zheev_uplo) def _allocate_phonon(self): primitive_lattice = np.linalg.inv(self._primitive.get_cell()) self._grid_address, self._bz_map, self._bz_to_pp_map = get_bz_grid_address( self._mesh, primitive_lattice, with_boundary=True, is_bz_map_to_pp=True) num_band = self._primitive.get_number_of_atoms() * 3 num_grid = len(self._grid_address) self._phonon_done = np.zeros(num_grid, dtype='byte') self._frequencies = np.zeros((num_grid, num_band), dtype='double') self._degenerates = np.zeros((num_grid, num_band), dtype="intc") self._eigenvectors = np.zeros((num_grid, num_band, num_band), dtype='complex128') def write_amplitude_all(self): if self.get_is_write_amplitude(): if self.get_amplitude_all() is not None: write_amplitude_to_hdf5_all(self.get_amplitude_all(), self._mesh, is_nosym=self.is_nosym()) self.set_is_read_amplitude(True) self.set_is_write_amplitude(False)