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)
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)