Exemplo n.º 1
0
    def test_kkr(self):
        energy, eps_real, eps_imag = self.ge_vasprun.dielectric

        de = (energy[10] - energy[0]) / 10

        def symmetrise(a):
            """Convert XX YY ZZ XY YZ XZ array to a symmetrical 3x3 matrix"""
            return [[a[0], a[3], a[5]], [a[4], a[1], a[4]], [a[5], a[4], a[2]]]

        eps_imag_3x3 = [symmetrise(a) for a in eps_imag]
        eps_real_3x3 = np.array([symmetrise(a) for a in eps_real])

        # Check difference between eps_real reported by Vasp and determined
        # by Kramers-Kronig transformation of eps_im.
        #
        # Some discrepancy is normal, check RMS is as expected
        # This is likely due to the limited precision available in vasprun

        error = kkr(de, eps_imag_3x3) - eps_real_3x3
        error_fracs = [
            eps / eps_ref
            for eps, eps_ref in zip(error.flatten(), eps_real_3x3.flatten())
            if eps_ref > 1e-2
        ]  # Exclude low-precision cases

        self.assertLess(np.sqrt((np.array(error_fracs)**2).mean()), 0.1)
Exemplo n.º 2
0
def dielectric_from_opt(filename, cshift=1e-6, out_filename=None):
    """Read a Questaal opt.ext file and return dielectric function

    opt.ext files only provide x, y, z components so the off-diagonal terms are
    set to zero.

    Args:
        filename (:obj:`float`):
            Path to ``opt.ext`` output of LMF optics calculation. If this is
            split into spin channels, the values will be recombined to a single
            (observable) channel.
        cshift (:obj:`float`, optional):
            A small imaginary element used in Kramers-Kronig integration.
        out_filename (:obj:`float`, optional):
            Path to write tabulated dielectric data with columns::

                energy(eV) real_xx real_yy real_zz imag_xx imag_yy imag_zz

            If None, no file is written.

    Returns:
        :obj:`tuple`
            The format imitates the ``dielectric`` attribute of
            :obj:`pymatgen.io.vasp.outputs.Vasprun`: a tuple of the form::

                ([energy1, energy2, ...],
                 [[real_xx_1, real_yy_1, real_zz_1,
                   real_xy_1, real_yz_1, real_xz_1],
                  [real_xx_2, real_yy_2, real_zz_2,
                   real_xy_2, real_yz_2, real_xz_2], ...],
                 [[imag_xx_1, imag_yy_1, imag_zz_1,
                   imag_xy_1, imag_yz_1, imag_xz_1],
                  [imag_xx_2, imag_yy_2, imag_zz_2,
                   imag_xy_2, imag_yz_2, imag_xz_2], ...])

            The off-diagonal (xy, yz, xz) terms are not given in ``opt.ext``
            and are set to zero. Only the imaginary part is provided so the
            real part is constructed by a Kramers-Kronig trasformation.
            Energy units are converted from Ry to eV.
    """

    data = np.genfromtxt(filename, skip_header=1)
    if data.shape[1] == 4:
        pass
    elif data.shape[1] == 7:
        data = np.hstack(data[:, :1], data[1:4] + data[4:7])
    else:
        raise ValueError("Not sure how to interpret {}; expected "
                         "4 or 7 columns.".format(filename))

    data[:, 0] *= _ry_to_ev
    de = data[1, 0] - data[0, 0]
    eps_imag = [[[row[1], 0, 0], [0, row[2], 0], [0, 0, row[3]]]
                for row in data]
    eps_real = kkr(de, eps_imag)

    # Re-shape to XX YY ZZ XY YZ XZ format
    eps_imag = np.array(eps_imag).reshape(len(eps_imag), 9)
    eps_imag = eps_imag[:, [0, 4, 8, 1, 5, 2]]
    eps_real = eps_real.reshape(len(eps_real), 9)
    eps_real = eps_real[:, [0, 4, 8, 1, 5, 2]]

    if out_filename is not None:
        with open(out_filename, 'wt') as f:
            for e, r, i in zip(data[:, 0].flatten(), eps_real, eps_imag):
                f.write((' '.join(['{:10.8f}'] * 7)).format(e, *r[:3], *i[:3]))
                f.write('\n')

    return (data[:, 0].flatten(), eps_real, eps_imag)