Exemplo n.º 1
0
    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
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
    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)