Beispiel #1
0
    def plot_gruns_scatter(self,
                           values="gruns",
                           ax=None,
                           units="eV",
                           cmap="rainbow",
                           **kwargs):
        """
        A scatter plot of the values of the Gruneisen parameters or group velocities as a function
        of the phonon frequencies.

        Args:
            values: Define the plotted quantity. "gruns" for Grunesein parameters, "gruns_fd" for Grunesein
                parameters calculated with finite differences,  "groupv" for phonon group velocities.
            ax: |matplotlib-Axes| or None if a new figure should be created.
            units: Units for phonon frequencies. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            cmap: matplotlib colormap. If not None, points are colored according to the branch index.
            **kwargs: kwargs passed to the matplotlib function 'scatter'. Size defaults to 10.

        Returns: |matplotlib-Figure|
        """

        if values == "gruns":
            y = self.gvals_qibz
        elif values == "groupv":
            # TODO: units?
            y = np.linalg.norm(self.reader.read_value("gruns_dwdq_qibz"),
                               axis=-1)
        elif values == "gruns_fd":
            y = self.gvals_qibz_finite_differences(match_eigv=True)
        else:
            raise ValueError("Unsupported values: `%s`" % values)

        w = self.wvols_qibz[:, self.iv0, :] * abu.phfactor_ev2units(units)

        ax, fig, plt = get_ax_fig_plt(ax=ax)
        ax.grid(True)

        if 's' not in kwargs:
            kwargs['s'] = 10

        if cmap is None:
            ax.scatter(w.flatten(), y.flatten(), **kwargs)
        else:
            cmap = plt.get_cmap(cmap)
            natom3 = 3 * len(self.structure)
            for nu in range(natom3):
                color = cmap(float(nu) / natom3)
                ax.scatter(w[:, nu], y[:, nu], color=color, **kwargs)

        ax.set_xlabel('Frequency %s' % abu.phunit_tag(units))

        if values.startswith("gruns"):
            ax.set_ylabel('Gruneisen')
        elif values == "groupv":
            ax.set_ylabel('|v|')

        return fig
Beispiel #2
0
    def plot_fit_freqs_dir(self,
                           idir,
                           ax=None,
                           units="eV",
                           fontsize=8,
                           **kwargs):
        """
        Plots the phonon frequencies, if available, along the specified direction.
        The line representing the fitted value will be shown as well.

        Args:
            idir: index of the direction.
            ax: |matplotlib-Axes| or None if a new figure should be created.
            units: Units for phonon plots. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz"). Case-insensitive.
            fontsize: fontsize for legends and titles

        Returns: |matplotlib-Figure|
        """
        if self.phfreqs is None or self.qpts is None:
            raise ValueError(
                "The plot requires phonon frequencies and qpoints.")

        ax, fig, plt = get_ax_fig_plt(ax=ax)

        ax.margins(x=0, y=0)
        rlatt = self.structure.lattice.reciprocal_lattice
        freqs = self.phfreqs[idir]
        qpt_cart_coords = np.array([
            np.linalg.norm(rlatt.get_cartesian_coords(c))
            for c in self.qpts[idir]
        ])
        slope = self.sound_velocities[
            idir] / abu.velocity_at_to_si * bohr_to_angstrom / eV_to_Ha

        units_factor = abu.phfactor_ev2units(units)

        title = "[{:.3f}, {:.3f}, {:.3f}]".format(*self.directions[idir])
        if self.labels:
            title += " - {}".format(self.labels[idir])

        for i, c in enumerate(["r", "b", "g"]):
            ax.scatter(qpt_cart_coords,
                       freqs[i] * units_factor,
                       color=c,
                       marker="x")
            ax.plot(qpt_cart_coords,
                    slope[i] * qpt_cart_coords * units_factor,
                    color=c,
                    ls="-")

        ax.set_title(title, fontsize=fontsize)
        ax.grid(True)
        ax.set_xlabel("Wave Vector")
        ax.set_ylabel(abu.wlabel_from_units(units))

        return fig
Beispiel #3
0
    def test_units_api(self):
        for units in ["ev", "meV" ,"ha", "cm-1", "cm^-1", "Thz"]:
            assert abu.phfactor_ev2units(units)
            assert abu.phunit_tag(units)
            assert abu.phdos_label_from_units(units)
            assert abu.wlabel_from_units(units)

        for func in [abu.phfactor_ev2units, abu.phunit_tag, abu.phdos_label_from_units]:
            with self.assertRaises(KeyError):
                func("foo")
Beispiel #4
0
    def get_powder_intensity(self,
                             temp,
                             laser_freq,
                             non_anal_dir=None,
                             relative=False,
                             units="eV"):
        """
        Calculates the Raman intensities in arbitrary units for each mode integrated over all possible
        orientation to reproduce the powder measurements. It is possible to use the susceptibilities from
        transverse modes only or to specify one of the directions with non analytical contributions.

        Args:
            temp: temperature in K.
            laser_freq: frequency of the incident laser. The units are determined the "units"
                argument.
            non_anal_dir: index of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            relative: if True the intensities will be rescaled so that the largest value of the
                total intensity is 1.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")

        Returns:
            A PowderIntensity with the parallel, perpendicular and total components of the powder
            intensities. Each one is an array with length n modes.
        """

        if non_anal_dir is None:
            w = self.phfreqs
            sus = self.susceptibility
        else:
            w = self.non_anal_phfreqs[non_anal_dir]
            sus = self.non_anal_susceptibility[non_anal_dir]

        g0 = np.trace(sus, axis1=1, axis2=2)**2 / 3
        g1 = ((sus[:, 0, 1] - sus[:, 1, 0])**2 +
              (sus[:, 0, 2] - sus[:, 2, 0])**2 +
              (sus[:, 2, 1] - sus[:, 1, 2])**2) / 2
        g2 = ((sus[:, 0, 1] + sus[:, 1, 0])**2 + (sus[:, 0, 2] + sus[:, 2, 0])**2 + (sus[:, 2, 1] + sus[:, 1, 2])**2) / 2 + \
             ((sus[:, 0, 0] - sus[:, 1, 1])**2 + (sus[:, 0, 0] - sus[:, 2, 2])**2 + (sus[:, 1, 1] - sus[:, 2, 2])**2) / 3

        laser_freq = laser_freq / abu.phfactor_ev2units(units)

        c = self._get_prefactor(w=w, temp=temp, laser_freq=laser_freq)

        paral = c * (10 * g0 + 4 * g2)
        perp = c * (5 * g1 + 3 * g2)
        tot = paral + perp
        if relative:
            m = tot.max()
            paral /= m
            perp /= m
            tot /= m

        return PowderIntensity(paral, perp, tot)
Beispiel #5
0
    def plot_gruns_scatter(self, values="gruns", ax=None, units="eV", cmap="rainbow", **kwargs):
        """
        A scatter plot of the values of the Gruneisen parameters or group velocities as a function
        of the phonon frequencies.

        Args:
            values: Define the plotted quantity. "gruns" for Grunesein parameters, "gruns_fd" for Grunesein
                parameters calculated with finite differences,  "groupv" for phonon group velocities.
            ax: |matplotlib-Axes| or None if a new figure should be created.
            units: Units for phonon frequencies. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            cmap: matplotlib colormap. If not None, points are colored according to the branch index.
            **kwargs: kwargs passed to the matplotlib function 'scatter'. Size defaults to 10.

        Returns: |matplotlib-Figure|
        """

        if values == "gruns":
            y = self.gvals_qibz
        elif values == "groupv":
            # TODO: units?
            y = np.linalg.norm(self.reader.read_value("gruns_dwdq_qibz"), axis=-1)
        elif values == "gruns_fd":
            y = self.gvals_qibz_finite_differences(match_eigv=True)
        else:
            raise ValueError("Unsupported values: `%s`" % values)

        w = self.wvols_qibz[:, self.iv0, :] * abu.phfactor_ev2units(units)

        ax, fig, plt = get_ax_fig_plt(ax=ax)
        ax.grid(True)

        if 's' not in kwargs:
            kwargs['s'] = 10

        if cmap is None:
            ax.scatter(w.flatten(), y.flatten(), **kwargs)
        else:
            cmap = plt.get_cmap(cmap)
            natom3 = 3 * len(self.structure)
            for nu in range(natom3):
                color = cmap(float(nu) / natom3)
                ax.scatter(w[:, nu], y[:, nu], color=color, **kwargs)

        ax.set_xlabel('Frequency %s' % abu.phunit_tag(units))

        if values.startswith("gruns"):
            ax.set_ylabel('Gruneisen')
        elif values == "groupv":
            ax.set_ylabel('|v|')

        return fig
Beispiel #6
0
    def fit_to_frequency(self,
                         fit_function=None,
                         units="eV",
                         min_fit_eta=None,
                         max_fit_eta=None):
        """
        Uses the energies and the displacements to calculate the phonon frequency corresponding to the quadratic
        term of the fit.
        The fit is performed with scipy.optimize.curve_fit based on the function given in input and can also be
        limited number to a subset of the values of the displacements.


        Args:
            fit_function: a function that will be used to fit the data. The first parameter should be the coefficient
                of the quadratic term. If None a simple quadratic fit will be used.
            units: units of the output frequency. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            min_fit_eta: if not None represents minimum value allowed for the (signed) eta to be used in the fit.
            max_fit_eta: if not None represents maximum value allowed for the (signed) eta to be used in the fit.

        Returns:
            A namedtuple with 'freq': the values of the frequency extracted from the fit,
            'fit_params': the parameters obtained from the fit, 'cov': the estimated covariance of fit_params
            (see scipy.optimize.curve_fit documentation for more details).
        """

        if self.energies is None:
            raise ValueError("The energies are required to calculate the fit")

        if min_fit_eta is None:
            min_fit_eta = self.etas.min()
        if max_fit_eta is None:
            max_fit_eta = self.etas.max()

        indices = np.where((min_fit_eta <= self.etas)
                           & (self.etas <= max_fit_eta))

        if fit_function is None:
            fit_function = quadratic_fit_function

        etas = self.etas[indices]
        energies = np.array(self.energies)[indices]

        params, cov = optimize.curve_fit(fit_function, etas, energies)

        # frequency in eV.
        freq = self._quad_coeff_to_freq(params[0])

        return dict2namedtuple(freq=freq * phfactor_ev2units(units),
                               fit_params=params,
                               cov=cov)
Beispiel #7
0
    def _get_lorentz_freqs_and_factor(self, intensity, non_anal_dir, min_freq,
                                      max_freq, num, width, units):
        """
        Helper method to get the list of frequencies and the main spread factors to
        calculate the broadened Raman intensities with a Lorentz distribution.

        Args:
            intensity: the Raman intensities at specified frequencies.
            non_anal_dir: ndex of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            min_freq: minimum frequency considered. If None it will be given by the minimum
                frequency with non zero intensities minus 10 times the width of the distribution.
            max_freq: maximum frequency considered. If None it will be given by the maximum
                frequency with non zero intensities plus 10 times the width of the distribution.
            width: the width of the Lorentz distribution.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")

        Returns:
            Tuple with list of "num" frequencies in eV and factors for the Lorentz broadening
            with shape (n modes, num).
        """

        if non_anal_dir is None:
            w = self.phfreqs
        else:
            w = self.non_anal_phfreqs[non_anal_dir]

        units_factor = abu.phfactor_ev2units(units)

        width = width / units_factor

        if min_freq is None:
            min_ind = np.where(intensity / intensity.max() > 1e-10)[0].min()
            min_freq = w[min_ind] - 10 * width
        else:
            min_freq = min_freq / units_factor

        if max_freq is None:
            max_ind = np.where(intensity / intensity.max() > 1e-10)[0].max()
            max_freq = w[max_ind] + 10 * width
        else:
            max_freq = max_freq / units_factor

        freqs = np.linspace(min_freq, max_freq, num)

        lorentz = width / ((freqs - w.reshape((-1, 1)))**2 + width**2) / np.pi

        return freqs, lorentz
Beispiel #8
0
    def plot_gruns_scatter(self,
                           values="gruns",
                           ax=None,
                           units="eV",
                           **kwargs):
        """
        A scatter plot of the values of the Gruneisen parameters or group velocities as a function
        of the phonon frequencies.

        Args:
            values:  Define the plotted quantity. "gruns" for Grunesein parameters,
                "groupv" for phonon group velocities.
            ax: |matplotlib-Axes| or None if a new figure should be created.
            units: Units for phonon frequencies. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            **kwargs: kwargs passed to the matplotlib function 'scatter'. Size defaults to 10.

        Returns: |matplotlib-Figure|
        """

        if values == "gruns":
            y = self.gvals_qibz
        elif values == "groupv":
            # TODO: units?
            y = np.linalg.norm(self.reader.read_value("gruns_dwdq_qibz"),
                               axis=-1)
        else:
            raise ValueError("Unsupported values: `%s`" % values)

        w = self.wvols_qibz[:, self.iv0, :] * abu.phfactor_ev2units(units)

        ax, fig, plt = get_ax_fig_plt(ax=ax)

        if 's' not in kwargs:
            kwargs['s'] = 10

        ax.scatter(w.flatten(), y.flatten(), **kwargs)
        ax.set_xlabel('Frequency %s' % abu.phunit_tag(units))
        if values == "gruns":
            ax.set_ylabel('Gruneisen')
        elif values == "groupv":
            ax.set_ylabel('|v|')

        return fig
Beispiel #9
0
    def plot_gruns_scatter(self, values="gruns", ax=None, units="eV", **kwargs):
        """
        A scatter plot of the values of the Gruneisen parameters or group velocities as a function
        of the phonon frequencies.

        Args:
            values:  Define the plotted quantity. "gruns" for Grunesein parameters,
                "groupv" for phonon group velocities.
            ax: |matplotlib-Axes| or None if a new figure should be created.
            units: Units for phonon frequencies. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            **kwargs: kwargs passed to the matplotlib function 'scatter'. Size defaults to 10.

        Returns: |matplotlib-Figure|
        """

        if values == "gruns":
            y = self.gvals_qibz
        elif values == "groupv":
            # TODO: units?
            y = np.linalg.norm(self.reader.read_value("gruns_dwdq_qibz"), axis=-1)
        else:
            raise ValueError("Unsupported values: `%s`" % values)

        w = self.wvols_qibz[:, self.iv0, :] * abu.phfactor_ev2units(units)

        ax, fig, plt = get_ax_fig_plt(ax=ax)

        if 's' not in kwargs:
            kwargs['s'] = 10

        ax.scatter(w.flatten(), y.flatten(), **kwargs)
        ax.set_xlabel('Frequency %s' % abu.phunit_tag(units))
        if values == "gruns":
            ax.set_ylabel('Gruneisen')
        elif values == "groupv":
            ax.set_ylabel('|v|')

        return fig
Beispiel #10
0
    def test_base(self):
        """Base tests for PhononBands"""
        filename = abidata.ref_file("trf2_5.out_PHBST.nc")
        phbands = PhononBands.from_file(filename)
        repr(phbands); str(phbands)
        assert phbands.to_string(title="Title", with_structure=False, with_qpoints=True, verbose=1)

        assert PhononBands.as_phbands(phbands) is phbands
        with self.assertRaises(TypeError):
            PhononBands.as_phbands({})
        assert np.array_equal(PhononBands.as_phbands(filename).phfreqs, phbands.phfreqs)

        with abilab.abiopen(abidata.ref_file("trf2_5.out_PHBST.nc")) as nc:
            repr(nc); str(nc)
            assert nc.to_string(verbose=1)
            assert nc.params["nqpt"] == len(nc.qpoints)
            assert nc.qpoints.is_path
            assert any(q.name is not None for q in nc.qpoints)
            same_phbands_nc = PhononBands.as_phbands(nc)
            self.assert_equal(same_phbands_nc.phfreqs, phbands.phfreqs)
            assert phbands.phdispl_cart.shape == (phbands.nqpt, phbands.num_branches, phbands.num_branches)
            # a + b gives plotter
            assert hasattr(same_phbands_nc + phbands, "combiplot")

        self.serialize_with_pickle(phbands, protocols=[-1], test_eq=False)

        # From pickle file.
        tmp_path = self.get_tmpname(suffix=".pickle")
        with open(tmp_path, "wb") as fh:
            pickle.dump(phbands, fh)
        same_phbands = PhononBands.as_phbands(tmp_path)
        self.assert_equal(same_phbands.phfreqs, phbands.phfreqs)

        # a + b + c gives plotter
        p = phbands + same_phbands + same_phbands_nc
        assert hasattr(p, "combiplot")

        assert phbands.minfreq == 0.0
        #self.assertEqual(phbands.maxfreq, 30)
        assert phbands.phfactor_ev2units("eV") == abu.phfactor_ev2units("eV")

        # Test XYZ vib
        phbands.create_xyz_vib(iqpt=0, filename=self.get_tmpname(text=True), max_supercell=[4, 4, 4])
        # Test ascii file
        phbands.create_ascii_vib(iqpts=0, filename=self.get_tmpname(text=True), pre_factor=1)
        # Test phononwebsite file
        phbands.create_phononwebsite_json(filename=self.get_tmpname(text=True), name='test')
        assert phbands.view_phononwebsite(verbose=1, dryrun=True) == 0
        # Test xmgrace
        phbands.to_xmgrace(self.get_tmpname(text=True))
        #phbands.to_xmgrace(sys.stdout)

        df = phbands.get_dataframe()
        assert "freq" in df and "mode" in df
        self.assert_almost_equal(df["freq"].values.min(), 0)

        umodes = phbands.get_unstable_modes(below_mev=-1000)
        assert len(umodes) == 0

        acoustic_modes = phbands.acoustic_indices((0, 0, 0))
        self.assertArrayEqual(acoustic_modes, [0, 1, 2])
        asr_breaking = phbands.asr_breaking()
        assert asr_breaking.absmax_break == 0

        # Test convertion to eigenvectors. Verify that they are orthonormal
        # Allow relatively large tolerance due to possible mismatching in the atomic masses between abinit and pmg
        # (Note that amu is None here)
        assert phbands.amu is None
        eig = phbands.dyn_mat_eigenvect
        assert len(eig) == phbands.nqpt

        cidentity = np.eye(len(eig[0]), dtype=np.complex)
        for iq in range(len(eig)):
            #print("About to test iq", iq, np.dot(eig[iq], eig[iq].T))
            #assert np.allclose(np.dot(eig[iq], eig[iq].T), cidentity , atol=1e-5, rtol=1e-3)
            assert np.allclose(np.dot(eig[iq].conjugate().T, eig[iq]), cidentity , atol=1e-5, rtol=1e-3)
            #self.assert_almost_equal(np.dot(eig[iq].conjugate().T, eig[iq]), cidentity)

        # Mapping reduced coordinates -> labels
        qlabels = {
            (0,0,0): r"$\Gamma$",
            (0.375, 0.375, 0.75): "K",
            (0.5, 0.5, 1.0): "X",
            (0.5, 0.5, 0.5): "L",
            (0.5, 0.0, 0.5): "X",
            (0.5, 0.25, 0.75): "W",
        }

        if self.has_matplotlib():
            assert phbands.plot(units="Thz", show=False)
            assert phbands.plot_fatbands(units="ha", qlabels=qlabels, show=False)
            assert phbands.plot_fatbands(phdos_file=abidata.ref_file("trf2_5.out_PHDOS.nc"), units="thz", show=False)
            assert phbands.plot_colored_matched(units="cm^-1", show=False)
            assert phbands.plot_phdispl(qpoint=(0, 0, 0), units="cm^-1", hatches=None, show=False)
            assert phbands.plot_phdispl(qpoint=(0, 0, 0), units="cm^-1", hatches=None, show=False, cart_dir="x+y")
            assert phbands.plot_phdispl(qpoint=(0, 0, 0), units="cm^-1", hatches=None, show=False, use_sqrt=True,
                                        normalize=False)
            with self.assertRaises(ValueError):
                # No LO-TO terms
                assert phbands.plot_phdispl(qpoint=1, is_non_analytical_direction=True, show=False)
            assert phbands.plot_phdispl_cartdirs(qpoint=0, units="cm^-1", show=False)
            assert phbands.boxplot(units="ev", mode_range=[2, 4], show=False)

        # Cannot compute PHDOS with q-path
        with self.assertRaises(ValueError):
            phdos = phbands.get_phdos()

        # convert to pymatgen object
        phbands.to_pymatgen()

        # get frozen phonons
        phbands.get_frozen_phonons((0.5, 0.5, 1.0), 1, eta=0.5, max_supercell=[5,5,5])

        assert not phbands.has_linewidths
        phbands.linewidths = np.ones(phbands.shape)
        assert phbands.has_linewidths
Beispiel #11
0
    def test_base(self):
        """Base tests for PhononBands"""
        filename = abidata.ref_file("trf2_5.out_PHBST.nc")
        phbands = PhononBands.from_file(filename)
        repr(phbands)
        str(phbands)
        assert phbands.to_string(title="Title",
                                 with_structure=False,
                                 with_qpoints=True,
                                 verbose=1)

        assert PhononBands.as_phbands(phbands) is phbands
        with self.assertRaises(TypeError):
            PhononBands.as_phbands({})
        assert np.array_equal(
            PhononBands.as_phbands(filename).phfreqs, phbands.phfreqs)

        with abilab.abiopen(abidata.ref_file("trf2_5.out_PHBST.nc")) as nc:
            repr(nc)
            str(nc)
            assert nc.to_string(verbose=1)
            assert nc.params["nqpt"] == len(nc.qpoints)
            assert nc.qpoints.is_path
            assert any(q.name is not None for q in nc.qpoints)
            same_phbands_nc = PhononBands.as_phbands(nc)
            self.assert_equal(same_phbands_nc.phfreqs, phbands.phfreqs)
            assert phbands.phdispl_cart.shape == (phbands.nqpt,
                                                  phbands.num_branches,
                                                  phbands.num_branches)
            # a + b gives plotter
            assert hasattr(same_phbands_nc + phbands, "combiplot")
            assert phbands.epsinf is None and phbands.zcart is None

        self.serialize_with_pickle(phbands, protocols=[-1], test_eq=False)

        # From pickle file.
        tmp_path = self.get_tmpname(suffix=".pickle")
        with open(tmp_path, "wb") as fh:
            pickle.dump(phbands, fh)
        same_phbands = PhononBands.as_phbands(tmp_path)
        self.assert_equal(same_phbands.phfreqs, phbands.phfreqs)

        # a + b + c gives plotter
        p = phbands + same_phbands + same_phbands_nc
        assert hasattr(p, "combiplot")

        assert phbands.minfreq == 0.0
        #self.assertEqual(phbands.maxfreq, 30)
        assert phbands.phfactor_ev2units("eV") == abu.phfactor_ev2units("eV")

        # Test XYZ vib
        phbands.create_xyz_vib(iqpt=0,
                               filename=self.get_tmpname(text=True),
                               max_supercell=[4, 4, 4])
        # Test ascii file
        phbands.create_ascii_vib(iqpts=0,
                                 filename=self.get_tmpname(text=True),
                                 pre_factor=1)
        # Test phononwebsite file
        phbands.create_phononwebsite_json(filename=self.get_tmpname(text=True),
                                          name='test')
        assert phbands.view_phononwebsite(verbose=1, dryrun=True) == 0
        # Test xmgrace
        phbands.to_xmgrace(self.get_tmpname(text=True))
        #phbands.to_xmgrace(sys.stdout)

        df = phbands.get_dataframe()
        assert "freq" in df and "mode" in df
        self.assert_almost_equal(df["freq"].values.min(), 0)

        umodes = phbands.get_unstable_modes(below_mev=-1000)
        assert len(umodes) == 0

        acoustic_modes = phbands.acoustic_indices((0, 0, 0))
        self.assertArrayEqual(acoustic_modes, [0, 1, 2])
        asr_breaking = phbands.asr_breaking()
        assert asr_breaking.absmax_break == 0

        # Test convertion to eigenvectors. Verify that they are orthonormal
        # Allow relatively large tolerance due to possible mismatching in the atomic masses between abinit and pmg
        # (Note that amu is None here)
        assert phbands.amu is None
        eig = phbands.dyn_mat_eigenvect
        assert len(eig) == phbands.nqpt

        cidentity = np.eye(len(eig[0]), dtype=np.complex)
        for iq in range(len(eig)):
            #print("About to test iq", iq, np.dot(eig[iq], eig[iq].T))
            #assert np.allclose(np.dot(eig[iq], eig[iq].T), cidentity , atol=1e-5, rtol=1e-3)
            assert np.allclose(np.dot(eig[iq].conjugate().T, eig[iq]),
                               cidentity,
                               atol=1e-5,
                               rtol=1e-3)
            #self.assert_almost_equal(np.dot(eig[iq].conjugate().T, eig[iq]), cidentity)

        # Mapping reduced coordinates -> labels
        qlabels = {
            (0, 0, 0): r"$\Gamma$",
            (0.375, 0.375, 0.75): "K",
            (0.5, 0.5, 1.0): "X",
            (0.5, 0.5, 0.5): "L",
            (0.5, 0.0, 0.5): "X",
            (0.5, 0.25, 0.75): "W",
        }

        if self.has_matplotlib():
            assert phbands.plot(units="Thz", show=False, temp=300)
            assert phbands.plot_fatbands(units="ha",
                                         qlabels=qlabels,
                                         show=False)
            assert phbands.plot_fatbands(
                phdos_file=abidata.ref_file("trf2_5.out_PHDOS.nc"),
                units="thz",
                show=False)
            assert phbands.plot_colored_matched(units="cm^-1", show=False)
            assert phbands.plot_phdispl(qpoint=(0, 0, 0),
                                        units="cm^-1",
                                        hatches=None,
                                        show=False)
            assert phbands.plot_phdispl(qpoint=(0, 0, 0),
                                        units="cm^-1",
                                        hatches=None,
                                        show=False,
                                        cart_dir="x+y")
            assert phbands.plot_phdispl(qpoint=(0, 0, 0),
                                        units="cm^-1",
                                        hatches=None,
                                        show=False,
                                        use_sqrt=True,
                                        normalize=False)
            with self.assertRaises(ValueError):
                # No LO-TO terms
                assert phbands.plot_phdispl(qpoint=1,
                                            is_non_analytical_direction=True,
                                            show=False)
            assert phbands.plot_phdispl_cartdirs(qpoint=0,
                                                 units="cm^-1",
                                                 show=False)
            assert phbands.boxplot(units="ev", mode_range=[2, 4], show=False)

        # Cannot compute PHDOS with q-path
        with self.assertRaises(ValueError):
            phdos = phbands.get_phdos()

        # convert to pymatgen object
        phbands.to_pymatgen()

        # get frozen phonons
        phbands.get_frozen_phonons((0.5, 0.5, 1.0),
                                   1,
                                   eta=0.5,
                                   max_supercell=[5, 5, 5])

        assert not phbands.has_linewidths
        phbands.linewidths = np.ones(phbands.shape)
        assert phbands.has_linewidths
Beispiel #12
0
    def plot(self,
             components="upper",
             view="inequivalent",
             units="eV",
             select_symbols=None,
             xlims=None,
             ylims=None,
             sharey=False,
             fontsize=8,
             verbose=0,
             **kwargs):
        """
        Plot the generalized phonon DOS g_ij(omega, atom) for each atom in the unit cell.
        One subplot per atom. Each subplot shows the 9 independent components of the symmetric tensor.
        as a function of frequency. By default, only "inequivalent" atoms are shown.

        Args:
            view: "inequivalent" to show only inequivalent atoms. "all" for all sites.
            components: List of cartesian tensor components to plot e.g. ["xx", "xy"].
                "all" for all components. "upper" for the upper triangle, "diag" for diagonal elements.
            units: Units energy axis. Possible values in ("eV", "meV", "Ha", "cm-1", "Thz").
                Case-insensitive.
            select_symbols: String or list of strings with chemical symbols. Used to select only atoms of this type.
            xlims: Set the data limits for the x-axis. Accept tuple e.g. ``(left, right)``
                   or scalar e.g. ``left``. If left (right) is None, default values are used.
            ylims: Set the data limits for the y-axis.
            sharey: True if y-axis should be shared.
            fontsize: Legend and title fontsize.
            verbose: Verbosity level.

        Returns: |matplotlib-Figure|
        """
        # TODO Decide units for internal arrays.
        factor = abu.phfactor_ev2units(units)

        # Select atoms.
        aview = self._get_atomview(view, select_symbols, verbose=verbose)

        num_plots = len(aview.iatom_list)
        nrows, ncols = 1, 1
        if num_plots > 1:
            ncols = 2
            nrows = num_plots // ncols + num_plots % ncols

        ax_list, fig, plt = get_axarray_fig_plt(None,
                                                nrows=nrows,
                                                ncols=ncols,
                                                sharex=True,
                                                sharey=sharey,
                                                squeeze=True)
        ax_list = np.reshape(ax_list, (nrows, ncols)).ravel()
        # don't show the last ax if num_plots is odd.
        if num_plots % ncols != 0: ax_list[-1].axis("off")

        xx = self.wmesh * factor
        components = self._get_components(components)

        # For each atom in the view.
        for ix, (ax, iatom, site_label) in enumerate(
                zip(ax_list, aview.iatom_list, aview.site_labels)):
            irow, icol = divmod(ix, ncols)
            ax.grid(True)
            set_axlims(ax, xlims, "x")
            set_axlims(ax, ylims, "y")
            ax.set_title(site_label, fontsize=fontsize)
            #site = self.structure[iatom]
            #color = cmap(float(iatom) / max((len(iatom_list) - 1), 1))

            # Plot components for this atom on the same ax.
            for c in components:
                yw = c.eval33w(self.values[iatom])
                label = r"$G_{%s}$" % c.name if ix == 0 else None
                ax.plot(xx, yw / factor, label=label, **c.plot_kwargs)

            # Handle labels.
            if irow == nrows - 1:
                ax.set_xlabel('Frequency %s' % abu.phunit_tag(units))
            else:
                set_visible(ax, False, "xlabel", "xticklabels")

            if ix == 0:
                ax.set_ylabel(r"$g_{ij}(\omega)$ 1/%s (Cart coords)" %
                              abu.phunit_tag(units))
                ax.legend(loc="best", fontsize=fontsize, shadow=True)

        return fig
Beispiel #13
0
    def get_modes_intensities(self,
                              temp,
                              laser_freq,
                              non_anal_dir=None,
                              relative=False,
                              units="eV",
                              pol_in=None,
                              pol_out=None):
        """
        Calculates the Raman intensities for each mode in arbitrary units. It is possible to use the
        susceptibilities from the transverse modes only or to specify one of the directions with non
        analytical contributions. By default it returns an array with shape (n modes, 3, 3) where each
        component of the 3x3 matrix are those with different incoming (the first index) and outgoing
        (the second index) polarization. These are along the components along the standard axes (e.g.
        "xx" or "yz" components). If pol_in and pol_out an array with shape (n modes) is returned
        corresponding to the selected polarizations.

        Args:
            temp: temperature in K.
            laser_freq: frequency of the incident laser. The units are determined the "units"
                argument.
            non_anal_dir: index of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            relative: if True the intensities will be rescaled so that the largest value is 1.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")
            pol_in: the polarization of the incoming photon. If not None can be either either a string
                with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements
                representing the polarization direction. If not None pol_out can not be None.
            pol_out: the polarization of the outgoing photon. If not None can be either either a string
                with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements
                representing the polarization direction. If not None pol_in can not be None.

        Returns:
            An array with the Raman intensities. If pol_in==pol_out==None has shape (n modes, 3, 3)
            with all the components. Otherwise an array with size (n modes) with the intensities of
            the selected polarizations.
        """

        if non_anal_dir is None:
            w = self.phfreqs
            sus = self.susceptibility
        else:
            w = self.non_anal_phfreqs[non_anal_dir]
            sus = self.non_anal_susceptibility[non_anal_dir]

        laser_freq = laser_freq / abu.phfactor_ev2units(units)

        c = self._get_prefactor(w=w, temp=temp, laser_freq=laser_freq)
        if pol_in is None and pol_out is None:
            i = c[:, np.newaxis, np.newaxis] * sus**2
            # this will make the indices of the i,j component such that the first
            # will refer to the polarization of the incoming photon and the second
            # to the polarization of the created one.
            np.transpose(i, axes=(0, 2, 1))
        else:
            if pol_in is None or pol_out is None:
                raise ValueError(
                    "pol_in and pol_out should be either both None or both defined"
                )
            dxyz = {"x": [1, 0, 0], "y": [0, 1, 0], "z": [0, 0, 1]}
            if isinstance(pol_in, str):
                pol_in = dxyz[pol_in.lower()]
            if isinstance(pol_out, str):
                pol_out = dxyz[pol_out.lower()]

            pol_in = np.array(pol_in) / np.linalg.norm(pol_in)
            pol_out = np.array(pol_out) / np.linalg.norm(pol_out)

            i = c * np.einsum("ijk, j, k -> i", sus, pol_out, pol_in)**2

        if relative:
            i /= i.max()

        return i
Beispiel #14
0
    def plot_intensity(self,
                       temp,
                       laser_freq,
                       width,
                       value,
                       non_anal_dir=None,
                       min_freq=None,
                       max_freq=None,
                       num=1000,
                       relative=False,
                       units="eV",
                       ax=None,
                       plot_phfreqs=False,
                       **kwargs):
        """
        Plot one representation of the broadened Raman intensities.

        Args:
            temp: temperature in K.
            laser_freq: frequency of the incident laser. The units are determined the "units"
                argument.
            width: the width of the Lorentz distribution. The units are determined the "units"
                argument. If None or 0 a plot of only the frequencies for each mode will be given.
            value: a string describing the value that should be plotted. Can be "powder" or
                a string of the type "xz" with the polarization of the incoming and outgoing
                phonon. All the combinations of "x", "y" and "z" are accepted.
            non_anal_dir: index of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            min_freq: minimum frequency considered. If None it will be given by the minimum
                frequency with non zero intensities minus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            max_freq: maximum frequency considered. If None it will be given by the maximum
                frequency with non zero intensities plus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            num: number of frequencies in the interval (min_freq, max_freq).
            relative: if True the intensities will be rescaled so that the largest value of the
                total intensity is 1.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")
            ax: |matplotlib-Axes| or None if a new figure should be created.
            plot_phfreqs: if True vertical dashed lines are added to the figure for all the
                phonon modes.
            **kwargs: arguments passed to the plot function.

        Returns:
            |matplotlib-Figure|
        """

        ax, fig, plt = get_ax_fig_plt(ax=ax)

        if width:
            if value == "powder":
                f = self.get_powder_lorentz_intensity(
                    temp=temp,
                    laser_freq=laser_freq,
                    width=width,
                    non_anal_dir=non_anal_dir,
                    min_freq=min_freq,
                    max_freq=max_freq,
                    num=num,
                    relative=relative,
                    units=units).tot
            else:
                pol_in = value[0]
                pol_out = value[1]
                f = self.get_lorentz_intensity(temp=temp,
                                               laser_freq=laser_freq,
                                               width=width,
                                               non_anal_dir=non_anal_dir,
                                               min_freq=min_freq,
                                               max_freq=max_freq,
                                               num=num,
                                               relative=relative,
                                               units=units,
                                               pol_in=pol_in,
                                               pol_out=pol_out)

            f.plot(ax=ax, **kwargs)

            if plot_phfreqs:
                if non_anal_dir is None:
                    w = self.phfreqs
                else:
                    w = self.non_anal_phfreqs[non_anal_dir]

                w = w * abu.phfactor_ev2units(units)

                min_freq = f.mesh[0]
                max_freq = f.mesh[-1]

                for wi in w:
                    if min_freq < wi < max_freq:
                        ax.axvline(x=wi, ls="--", color="k", lw=0.5)

        else:
            if value == "powder":
                ri = self.get_powder_intensity(temp=temp,
                                               laser_freq=laser_freq,
                                               non_anal_dir=non_anal_dir,
                                               relative=relative,
                                               units=units)

                i = ri.tot
            else:
                pol_in = value[0]
                pol_out = value[1]
                i = self.get_modes_intensities(temp=temp,
                                               laser_freq=laser_freq,
                                               non_anal_dir=non_anal_dir,
                                               relative=relative,
                                               units=units,
                                               pol_in=pol_in,
                                               pol_out=pol_out)

            if non_anal_dir is None:
                w = self.phfreqs * abu.phfactor_ev2units(units)
            else:
                w = self.non_anal_phfreqs[
                    non_anal_dir] * abu.phfactor_ev2units(units)

            ax.stem(w, i, **kwargs)

        return fig
Beispiel #15
0
    def get_powder_lorentz_intensity(self,
                                     temp,
                                     laser_freq,
                                     width,
                                     non_anal_dir=None,
                                     min_freq=None,
                                     max_freq=None,
                                     num=1000,
                                     relative=False,
                                     units="eV"):
        """
        Calculates the broadened Raman intensities in arbitrary units integrated over all possible
        orientation to reproduce the powder measurements for frequencies in an interval. It is possible to
        use the susceptibilities from the transverse modes only or to specify one of the directions with non
        analytical contributions.

        Args:
            temp: temperature in K.
            laser_freq: frequency of the incident laser. The units are determined the "units"
                argument.
            width: the width of the Lorentz distribution. The units are determined the "units"
                argument.
            non_anal_dir: index of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            min_freq: minimum frequency considered. If None it will be given by the minimum
                frequency with non zero intensities minus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            max_freq: maximum frequency considered. If None it will be given by the maximum
                frequency with non zero intensities plus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            num: number of frequencies in the interval (min_freq, max_freq).
            relative: if True the intensities will be rescaled so that the largest value of the
                total intensity is 1.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")

        Returns:
            A PowderIntensity with the parallel, perpendicular and total components of the powder
            intensities. Each one is a Function1D with "num" points.
        """

        pi = self.get_powder_intensity(temp=temp,
                                       laser_freq=laser_freq,
                                       non_anal_dir=non_anal_dir,
                                       units=units)

        freqs, lorentz = self._get_lorentz_freqs_and_factor(
            intensity=pi.tot,
            non_anal_dir=non_anal_dir,
            min_freq=min_freq,
            max_freq=max_freq,
            num=num,
            width=width,
            units=units)

        lpi = np.array([i.dot(lorentz) for i in pi])
        if relative:
            lpi /= lpi[2].max()

        # now convert the frequencies to the desired units for the output
        x = freqs * abu.phfactor_ev2units(units)

        return PowderIntensity(*(Function1D(x, y) for y in lpi))
Beispiel #16
0
    def get_lorentz_intensity(self,
                              temp,
                              laser_freq,
                              width,
                              non_anal_dir=None,
                              min_freq=None,
                              max_freq=None,
                              num=1000,
                              relative=False,
                              units="eV",
                              pol_in=None,
                              pol_out=None):
        """
        Calculates the broadened Raman intensities in arbitrary units for frequencies in an interval. It is
        possible to use the susceptibilities from the transverse modes only or to specify one of the directions
        with non analytical contributions. By default it returns a 3x3 matrix where each component is a
        Function1D object with the Raman intensities with different incoming (the first index) and outgoing
        (the second index) polarization. These are along the components along the standard axes (e.g. "xx" or
        "yz" components). If pol_in and pol_out a single Function1D is returned corresponding to the selected
        polarizations.

        Args:
            temp: temperature in K.
            laser_freq: frequency of the incident laser. The units are determined the "units"
                argument.
            width: the width of the Lorentz distribution. The units are determined the "units"
                argument.
            non_anal_dir: index of the direction along which the non analytical contribution
                has been calculated. Corresponds to the indices in the non_anal_directions attribute.
            min_freq: minimum frequency considered. If None it will be given by the minimum
                frequency with non zero intensities minus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            max_freq: maximum frequency considered. If None it will be given by the maximum
                frequency with non zero intensities plus 10 times the width of the distribution.
                If given the units are determined by the "units" argument.
            num: number of frequencies in the interval (min_freq, max_freq).
            relative: if True the intensities will be rescaled so that the largest value is 1.
            units: the units in which the input and the output frequencies will be given.
                Possible values in ("eV", "meV", "Ha", "cm-1", "Thz")
            pol_in: the polarization of the incoming photon. If not None can be either either a string
                with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements
                representing the polarization direction. If not None pol_out can not be None.
            pol_out: the polarization of the outgoing photon. If not None can be either either a string
                with one of the cartesian components i.e. "x", "y", "z" or an array with 3 elements
                representing the polarization direction. If not None pol_in can not be None.

        Returns:
            If pol_in==pol_out==None a 3x3 list with a Function1D corresponding to the different
            components of the intensities. Otherwise a single Function1D with the  intensities of
            the selected polarizations. Each Function1D has "num" points.
        """

        i = self.get_modes_intensities(temp=temp,
                                       laser_freq=laser_freq,
                                       non_anal_dir=non_anal_dir,
                                       units=units,
                                       pol_in=pol_in,
                                       pol_out=pol_out)

        freqs, lorentz = self._get_lorentz_freqs_and_factor(
            intensity=i,
            non_anal_dir=non_anal_dir,
            min_freq=min_freq,
            max_freq=max_freq,
            num=num,
            width=width,
            units=units)

        # convert the frequencies to the desired units for the output
        x = freqs * abu.phfactor_ev2units(units)

        if pol_in is not None and pol_out is not None:
            li = np.dot(i, lorentz)
            if relative:
                li /= li.max()

            return Function1D(x, li)

        else:
            li = np.einsum("ij, ikl -> jkl", lorentz, i)

            li_func = [[None] * 3] * 3

            for i in range(3):
                for j in range(3):
                    y = li[:, i, j]
                    if relative:
                        y /= y.max()
                    li_func[i][j] = Function1D(x, y)

            return li_func