# coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.
"""
This module defines classes for parsing the FEFF output files.
Currently supports the xmu.dat, ldos.dat output files are for non-spin case.
"""
import re
from collections import OrderedDict, defaultdict
import numpy as np
from monty.io import zopen
from monty.json import MSONable
from pymatgen.core.periodic_table import Element
from pymatgen.electronic_structure.core import Orbital, Spin
from pymatgen.electronic_structure.dos import CompleteDos, Dos
from pymatgen.io.feff import Header, Potential, Tags
__author__ = "Alan Dozier, Kiran Mathew, Chen Zheng"
__credits__ = "Anubhav Jain, Shyue Ping Ong"
__copyright__ = "Copyright 2011, The Materials Project"
__version__ = "1.0.3"
__maintainer__ = "Alan Dozier"
__email__ = "adozier@uky.edu"
__status__ = "Beta"
__date__ = "April 7, 2013"
[docs]class LDos(MSONable):
"""
Parser for ldos files ldos01, ldos02, .....
"""
def __init__(self, complete_dos, charge_transfer):
"""
Args:
complete_dos (CompleteDos): complete dos object
charge_transfer (dict): computed charge transfer between atoms
dictionary
"""
self.complete_dos = complete_dos
self.charge_transfer = charge_transfer
[docs] @staticmethod
def from_file(feff_inp_file="feff.inp", ldos_file="ldos"):
"""
Creates LDos object from raw Feff ldos files by
by assuming they are numbered consecutively, i.e. ldos01.dat
ldos02.dat...
Args:
feff_inp_file (str): input file of run to obtain structure
ldos_file (str): output ldos file of run to obtain dos info, etc.
"""
header_str = Header.header_string_from_file(feff_inp_file)
header = Header.from_string(header_str)
structure = header.struct
nsites = structure.num_sites
parameters = Tags.from_file(feff_inp_file)
if "RECIPROCAL" in parameters:
pot_dict = dict()
pot_readstart = re.compile(".*iz.*lmaxsc.*xnatph.*xion.*folp.*")
pot_readend = re.compile(".*ExternalPot.*switch.*")
pot_inp = re.sub(r"feff.inp", r"pot.inp", feff_inp_file)
dos_index = 1
begin = 0
with zopen(pot_inp, "r") as potfile:
for line in potfile:
if len(pot_readend.findall(line)) > 0:
break
if begin == 1:
begin += 1
continue
if begin == 2:
z_number = int(line.strip().split()[0])
ele_name = Element.from_Z(z_number).name
if ele_name not in pot_dict:
pot_dict[ele_name] = dos_index
else:
pot_dict[ele_name] = min(dos_index, pot_dict[ele_name])
dos_index += 1
if len(pot_readstart.findall(line)) > 0:
begin = 1
else:
pot_string = Potential.pot_string_from_file(feff_inp_file)
dicts = Potential.pot_dict_from_string(pot_string)
pot_dict = dicts[0]
with zopen(ldos_file + "00.dat", "r") as fobject:
f = fobject.readlines()
efermi = float(f[0].split()[4])
dos_energies = []
ldos = {}
for i in range(1, len(pot_dict) + 1):
if len(str(i)) == 1:
ldos[i] = np.loadtxt("{}0{}.dat".format(ldos_file, i))
else:
ldos[i] = np.loadtxt("{}{}.dat".format(ldos_file, i))
for i in range(0, len(ldos[1])):
dos_energies.append(ldos[1][i][0])
all_pdos = []
vorb = {"s": Orbital.s, "p": Orbital.py, "d": Orbital.dxy, "f": Orbital.f0}
forb = {"s": 0, "p": 1, "d": 2, "f": 3}
dlength = len(ldos[1])
for i in range(nsites):
pot_index = pot_dict[structure.species[i].symbol]
all_pdos.append(defaultdict(dict))
for k, v in vorb.items():
density = [ldos[pot_index][j][forb[k] + 1] for j in range(dlength)]
updos = density
downdos = None
if downdos:
all_pdos[-1][v] = {Spin.up: updos, Spin.down: downdos}
else:
all_pdos[-1][v] = {Spin.up: updos}
pdos = all_pdos
vorb2 = {0: Orbital.s, 1: Orbital.py, 2: Orbital.dxy, 3: Orbital.f0}
pdoss = {structure[i]: {v: pdos[i][v] for v in vorb2.values()} for i in range(len(pdos))}
forb = {"s": 0, "p": 1, "d": 2, "f": 3}
tdos = [0] * dlength
for i in range(nsites):
pot_index = pot_dict[structure.species[i].symbol]
for v in forb.values():
density = [ldos[pot_index][j][v + 1] for j in range(dlength)]
for j in range(dlength):
tdos[j] = tdos[j] + density[j]
tdos = {Spin.up: tdos}
dos = Dos(efermi, dos_energies, tdos)
complete_dos = CompleteDos(structure, dos, pdoss)
charge_transfer = LDos.charge_transfer_from_file(feff_inp_file, ldos_file)
return LDos(complete_dos, charge_transfer)
[docs] @staticmethod
def charge_transfer_from_file(feff_inp_file, ldos_file):
"""
Get charge transfer from file.
Args:
feff_inp_file (str): name of feff.inp file for run
ldos_file (str): ldos filename for run, assume consequetive order,
i.e., ldos01.dat, ldos02.dat....
Returns:
dictionary of dictionaries in order of potential sites
({"p": 0.154, "s": 0.078, "d": 0.0, "tot": 0.232}, ...)
"""
cht = OrderedDict()
parameters = Tags.from_file(feff_inp_file)
if "RECIPROCAL" in parameters:
dicts = [dict()]
pot_dict = dict()
dos_index = 1
begin = 0
pot_inp = re.sub(r"feff.inp", r"pot.inp", feff_inp_file)
pot_readstart = re.compile(".*iz.*lmaxsc.*xnatph.*xion.*folp.*")
pot_readend = re.compile(".*ExternalPot.*switch.*")
with zopen(pot_inp, "r") as potfile:
for line in potfile:
if len(pot_readend.findall(line)) > 0:
break
if begin == 1:
z_number = int(line.strip().split()[0])
ele_name = Element.from_Z(z_number).name
if len(pot_dict) == 0:
pot_dict[0] = ele_name
elif len(pot_dict) > 0:
pot_dict[max(pot_dict.keys()) + 1] = ele_name
begin += 1
continue
if begin == 2:
z_number = int(line.strip().split()[0])
ele_name = Element.from_Z(z_number).name
dicts[0][ele_name] = dos_index
dos_index += 1
if len(pot_dict) == 0:
pot_dict[0] = ele_name
elif len(pot_dict) > 0:
pot_dict[max(pot_dict.keys()) + 1] = ele_name
if len(pot_readstart.findall(line)) > 0:
begin = 1
else:
pot_string = Potential.pot_string_from_file(feff_inp_file)
dicts = Potential.pot_dict_from_string(pot_string)
pot_dict = dicts[1]
for i in range(0, len(dicts[0]) + 1):
if len(str(i)) == 1:
with zopen("{}0{}.dat".format(ldos_file, i), "rt") as fobject:
f = fobject.readlines()
s = float(f[3].split()[2])
p = float(f[4].split()[2])
d = float(f[5].split()[2])
f1 = float(f[6].split()[2])
tot = float(f[1].split()[4])
cht[str(i)] = {pot_dict[i]: {"s": s, "p": p, "d": d, "f": f1, "tot": tot}}
else:
with zopen(ldos_file + str(i) + ".dat", "rt") as fid:
f = fid.readlines()
s = float(f[3].split()[2])
p = float(f[4].split()[2])
d = float(f[5].split()[2])
f1 = float(f[6].split()[2])
tot = float(f[1].split()[4])
cht[str(i)] = {pot_dict[i]: {"s": s, "p": p, "d": d, "f": f1, "tot": tot}}
return cht
[docs] def charge_transfer_to_string(self):
"""returns shrage transfer as string"""
ch = self.charge_transfer
chts = ["\nCharge Transfer\n\nabsorbing atom"]
for i in range(len(ch)):
for atom, v2 in ch[str(i)].items():
a = [
"\n",
atom,
"\n",
"s ",
str(v2["s"]),
"\n",
"p ",
str(v2["p"]),
"\n",
"d ",
str(v2["d"]),
"\n",
"f ",
str(v2["f"]),
"\n",
"tot ",
str(v2["tot"]),
"\n",
]
chts.extend(a)
return "".join(chts)
[docs]class Xmu(MSONable):
r"""
Parser for data in 'xmu.dat' file.
The file 'xmu.dat' contains XANES, EXAFS or NRIXS data depending on the
situation; \\mu, \\mu_0, and \\chi = \\chi * \\mu_0/ \\mu_0/(edge+50eV) as
functions of absolute energy E, relative energy E − E_f and wave number k.
Default attributes:
xmu: Photon absorption cross section of absorbing atom in material
Energies: Energies of data point
relative_energies: E - E_fermi
wavenumber: k=\\sqrt(E −E_fermi)
mu: The total absorption cross-section.
mu0: The embedded atomic background absorption.
chi: fine structure.
Edge: Aborption Edge
Absorbing atom: Species of absorbing atom
Material: Formula of material
Source: Source of structure
Calculation: Type of Feff calculation performed
"""
def __init__(self, header, parameters, absorbing_atom, data):
"""
Args:
header: Header object
parameters: Tags object
absorbing_atom (str/int): absorbing atom symbol or index
data (numpy.ndarray, Nx6): cross_sections
"""
self.header = header
self.parameters = parameters
self.absorbing_atom = absorbing_atom
self.data = np.array(data)
[docs] @staticmethod
def from_file(xmu_dat_file="xmu.dat", feff_inp_file="feff.inp"):
"""
Get Xmu from file.
Args:
xmu_dat_file (str): filename and path for xmu.dat
feff_inp_file (str): filename and path of feff.inp input file
Returns:
Xmu object
"""
data = np.loadtxt(xmu_dat_file)
header = Header.from_file(feff_inp_file)
parameters = Tags.from_file(feff_inp_file)
pots = Potential.pot_string_from_file(feff_inp_file)
# site index (Note: in feff it starts from 1)
if "RECIPROCAL" in parameters:
absorbing_atom = parameters["TARGET"]
# species symbol
else:
absorbing_atom = pots.splitlines()[3].split()[2]
return Xmu(header, parameters, absorbing_atom, data)
@property
def energies(self):
"""
Returns the absolute energies in eV.
"""
return self.data[:, 0]
@property
def relative_energies(self):
"""
Returns energy with respect to the fermi level.
E - E_f
"""
return self.data[:, 1]
@property
def wavenumber(self):
r"""
Returns The wave number in units of \\AA^-1. k=\\sqrt(E −E_f) where E is
the energy and E_f is the Fermi level computed from electron gas theory
at the average interstitial charge density.
"""
return self.data[:, 2]
@property
def mu(self):
"""
Returns the total absorption cross-section.
"""
return self.data[:, 3]
@property
def mu0(self):
"""
Returns the embedded atomic background absorption.
"""
return self.data[:, 4]
@property
def chi(self):
"""
Returns the normalized fine structure.
"""
return self.data[:, 5]
@property
def e_fermi(self):
"""
Returns the fermi level in eV.
"""
return self.energies[0] - self.relative_energies[0]
@property
def source(self):
"""
Returns source identification from Header file
"""
return self.header.source
@property
def calc(self):
"""
Returns type of Feff calculation, XANES or EXAFS
"""
return "XANES" if "XANES" in self.parameters else "EXAFS"
@property
def material_formula(self):
"""
Returns chemical formula of material from feff.inp file
"""
try:
form = self.header.formula
except IndexError:
form = "No formula provided"
return "".join(map(str, form))
@property
def edge(self):
"""
Returns excitation edge.
"""
return self.parameters["EDGE"]
[docs] def as_dict(self):
"""
Returns dict representations of Xmu object
"""
d = MSONable.as_dict(self)
d["data"] = self.data.tolist()
return d
[docs]class Eels(MSONable):
"""
Parse'eels.dat' file.
"""
def __init__(self, data):
"""
Args:
data (): Eels data.
"""
self.data = np.array(data)
@property
def energies(self):
"""
Returns the energies in eV.
"""
return self.data[:, 0]
@property
def total_spectrum(self):
"""
Returns the total eels spectrum.
"""
return self.data[:, 1]
@property
def atomic_background(self):
"""
Returns: atomic background.
"""
return self.data[:, 2]
@property
def fine_structure(self):
"""
Returns: Fine structure of EELS.
"""
return self.data[:, 3]
[docs] @staticmethod
def from_file(eels_dat_file="eels.dat"):
"""
Parse eels spectrum.
Args:
eels_dat_file (str): filename and path for eels.dat
Returns:
Eels object
"""
data = np.loadtxt(eels_dat_file)
return Eels(data)
[docs] def as_dict(self):
"""
Returns dict representations of Xmu object
"""
d = MSONable.as_dict(self)
d["data"] = self.data.tolist()
return d