def match_bands(self, displ, structure, amu): """ Match the phonon bands of neighboring q-points based on the scalar product of the eigenvectors """ eigenvectors = get_dyn_mat_eigenvec(displ, structure, amu) ind = np.zeros(displ.shape[0:2], dtype=np.int) ind[0] = range(displ.shape[1]) for i in range(1, len(eigenvectors)): match = match_eigenvectors(eigenvectors[i - 1], eigenvectors[i]) ind[i] = [match[m] for m in ind[i - 1]] return ind
def calculate_gruns_finite_differences(phfreqs, eig, iv0, volume, dv): """ Calculates the Gruneisen parameters from finite differences on the phonon frequencies. Uses the eigenvectors to match the frequencies at different volumes. Args: phfreqs: numpy array with the phonon frequencies at different volumes. Shape (nvols, nqpts, 3*natoms) eig: numpy array with the phonon eigenvectors at the different volumes. Shape (nvols, nqpts, 3*natoms, 3*natoms) If None simple ordering will be used. iv0: index of the 0 volume. volume: volume of the structure at the central volume. dv: volume variation. Returns: A numpy array with the gruneisen parameters. Shape (nqpts, 3*natoms) """ phfreqs = phfreqs.copy() nvols = phfreqs.shape[0] if eig is not None: for i in range(nvols): if i == iv0: continue for iq in range(phfreqs.shape[1]): ind = match_eigenvectors(eig[iv0, iq], eig[i, iq]) phfreqs[i, iq] = phfreqs[i, iq][ind] acc = nvols - 1 g = np.zeros_like(phfreqs[0]) for iq in range(phfreqs.shape[1]): for im in range(phfreqs.shape[2]): w = phfreqs[iv0, iq, im] if w == 0: g[iq, im] = 0 else: g[iq, im] = -finite_diff( phfreqs[:, iq, im], dv, order=1, acc=acc)[iv0] * volume / w return g
def calculate_gruns_finite_differences(phfreqs, eig, iv0, volume, dv): """ Calculates the Gruneisen parameters from finite differences on the phonon frequencies. Uses the eigenvectors to match the frequencies at different volumes. Args: phfreqs: numpy array with the phonon frequencies at different volumes. Shape (nvols, nqpts, 3*natoms) eig: numpy array with the phonon eigenvectors at the different volumes. Shape (nvols, nqpts, 3*natoms, 3*natoms) If None simple ordering will be used. iv0: index of the 0 volume. volume: volume of the structure at the central volume. dv: volume variation. Returns: A numpy array with the gruneisen parameters. Shape (nqpts, 3*natoms) """ phfreqs = phfreqs.copy() nvols = phfreqs.shape[0] if eig is not None: for i in range(nvols): if i == iv0: continue for iq in range(phfreqs.shape[1]): ind = match_eigenvectors(eig[iv0, iq], eig[i, iq]) phfreqs[i, iq] = phfreqs[i, iq][ind] acc = nvols - 1 g = np.zeros_like(phfreqs[0]) for iq in range(phfreqs.shape[1]): for im in range(phfreqs.shape[2]): w = phfreqs[iv0, iq, im] if w == 0: g[iq, im] = 0 else: g[iq, im] = - finite_diff(phfreqs[:, iq, im], dv, order=1, acc=acc)[iv0] * volume / w return g
def from_phbst(cls, phbst_path, ignore_neg_freqs=True, labels=None): """ Creates an instance of the object starting interpolating the acoustic frequencies from a PHBST netcdf file. The file should contain a series of directions starting from gamma and with the same number of points for each direction, as the one produced in the from_ddb method. Args: phbst_path: path to the PHBST netcdf file. ignore_neg_freqs (bool): if True points with negative frequencies will not be considered in the fit, in order to ignore inaccuracies in the long range behavior. labels (list): list of string with the name of the directions. Returns: an instance of SoundVelocity """ phb = PhononBands.from_file(phbst_path) structure = phb.structure rlatt = structure.lattice.reciprocal_lattice # q points in cartesian coordinate in 1/bohr, the original units are 1/A qpt_cart_coords = [ rlatt.get_cartesian_coords(c) * bohr_to_angstrom for c in phb.qpoints.frac_coords ] qpt_cart_norms = np.linalg.norm(qpt_cart_coords, axis=1) # find the indices of the gamma points gamma_ind = [] for i, q in enumerate(phb.qpoints.frac_coords): if np.array_equal(q, [0, 0, 0]): gamma_ind.append(i) n_directions = len(gamma_ind) n_points = len(phb.qpoints) / n_directions if not n_points.is_integer(): raise ValueError( 'Error extracting information from {}'.format(phbst_path)) n_points = int(n_points) phfreqs = phb.phfreqs eigdisp = phb.phdispl_cart sound_velocities = [] mode_types = [] directions = [] all_acoustic_freqs = [] all_qpts = [] for i in range(n_directions): start = n_points * i # index of the end point used for the slice # (the position of the last point is actually end-1) end = n_points * (i + 1) dir_freqs = phfreqs[start:end] dir_displ = eigdisp[start:end] # matching bands dir_eigv = get_dyn_mat_eigenvec(dir_displ, structure, amu=phb.amu) n_freqs = 3 * len(structure) ind_match = np.zeros((n_points, n_freqs), dtype=np.int) ind_match[0] = range(n_freqs) for j in range(1, n_points): k = j - 1 match = match_eigenvectors(dir_eigv[k], dir_eigv[j]) ind_match[j] = [match[m] for m in ind_match[k]] acoustic_freqs = (dir_freqs[np.arange(n_points)[:, None], ind_match])[:, 0:3] acoustic_displ = (dir_displ[np.arange(n_points)[:, None], ind_match])[:, 0:3] direction = phb.qpoints[end - 1].frac_coords direction = np.array(direction) / np.linalg.norm(direction) directions.append(direction) # identify the first (not gamma) qpoint with all positive frequencies first_positive_freq_ind = None for j in range(1, n_points): if min(acoustic_freqs[j]) > 0: first_positive_freq_ind = j break if first_positive_freq_ind is None or first_positive_freq_ind - n_points / 2 > 0: raise ValueError( "too many negative frequencies along direction {}".format( direction)) sv = [] mt = [] cart_versor = qpt_cart_coords[end - 1] / np.linalg.norm( qpt_cart_coords[end - 1]) for k in range(3): start_fit = 0 if ignore_neg_freqs and first_positive_freq_ind > 1: start_fit = first_positive_freq_ind slope, se, _, _ = np.linalg.lstsq( qpt_cart_norms[start + start_fit:end][:, np.newaxis], acoustic_freqs[start_fit:, k] * eV_to_Ha, rcond=None) sv.append(slope[0] * abu.velocity_at_to_si) # identify the type of the mode (longitudinal/transversal) based on the # scalar product between the eigendisplacement and the direction. disp_0 = acoustic_displ[first_positive_freq_ind + 1, k, 0:3] disp_0 = disp_0 / np.linalg.norm(disp_0) scalar_prod = np.abs(np.dot(disp_0, cart_versor)) if scalar_prod > 0.9: mt.append("longitudinal") elif scalar_prod < 0.1: mt.append("transversal") else: mt.append(None) # sort the lists based on the sound velocites sv, mt, freqs = zip(*sorted(zip(sv, mt, acoustic_freqs.T))) sound_velocities.append(sv) mode_types.append(mt) all_acoustic_freqs.append(freqs) all_qpts.append(phb.qpoints.frac_coords[start:end]) return cls(directions=directions, sound_velocities=sound_velocities, mode_types=mode_types, structure=structure, labels=labels, phfreqs=all_acoustic_freqs, qpts=all_qpts)