Source code for pymatgen.io.feff.outputs

# coding: utf-8
# Copyright (c) Pymatgen Development Team.
# Distributed under the terms of the MIT License.


from collections import defaultdict, OrderedDict
import re

import numpy as np

from monty.io import zopen
from monty.json import MSONable

from pymatgen import Orbital, Spin, Element
from pymatgen.electronic_structure.dos import Dos, CompleteDos
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"

"""
This module defines classes for parsing the FEFF output files.

Currently supports the xmu.dat, ldos.dat output files are for non-spin case.
"""


[docs]class LDos(MSONable): """ Parser for ldos files ldos01, ldos02, ..... Args: complete_dos (CompleteDos): complete dos object charge_transfer (dict): computed charge transfer between atoms dictionary """ def __init__(self, complete_dos, charge_transfer): 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): """ 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. Args: header: Header object parameters: Tags object absorbing_atom (str/int): absorbing atom symbol or index data (numpy.ndarray, Nx6): cross_sections 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): 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): """ 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): 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): return self.data[:, 2] @property def fine_structure(self): 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