###############################################################################
# Copyright (c), Forschungszentrum Jülich GmbH, IAS-1/PGI-1, Germany. #
# All rights reserved. #
# This file is part of the Masci-tools package. #
# (Material science tools) #
# #
# The code is hosted on GitHub at https://github.com/judftteam/masci-tools. #
# For further information on the license, see the LICENSE.txt file. #
# For further information please visit http://judft.de/. #
# #
###############################################################################
"""
Here I collect all functions needed to parse the output of a KKR calculation.
These functions do not need aiida and are therefore separated from the actual
parser file where parse_kkr_outputfile is called
"""
import numpy as np
from numpy import ndarray, array, loadtxt, shape
from masci_tools.io.common_functions import (search_string, get_version_info, angles_to_vec,
get_corestates_from_potential, get_highest_core_state, convert_to_pystd,
get_outfile_txt)
from masci_tools.io.common_functions import get_Ry2eV
import traceback
__copyright__ = ('Copyright (c), 2017, Forschungszentrum Jülich GmbH,'
'IAS-1/PGI-1, Germany. All rights reserved.')
__license__ = 'MIT license, see LICENSE.txt file'
__contributors__ = 'Philipp Rüßmann, David Antognini Silva'
__version__ = '1.8.3'
####################################################################################
[docs]def parse_array_float(outfile, searchstring, splitinfo, replacepair=None, debug=False):
"""
Search for keyword `searchstring` in `outfile` and extract array of results
.. note:
`splitinfo` can be of the form [1, 'X', 1] or [2, 'X', 1, 0] where
splitinfo[0] can only be 1 or 2 (determines the mode),
splitinfo[1] is the string at which the line is split,
splitinfo[2] is the index which is used,
splitinfo[3] (only for splitinfo[0]==2) is the part that is taken after applying split() a second time (split at whitespace).
.. note:
If `replacepair` is not None the substring replacepair[0] is replaced by replacepair[1] before processing further
Returns: array of results
"""
tmptxt = get_outfile_txt(outfile)
itmp = 0
res = []
while itmp >= 0:
itmp = search_string(searchstring, tmptxt)
if debug:
print(('in parse_array_float (itmp, searchstring, outfile):', itmp, searchstring, outfile))
if itmp >= 0:
tmpval = tmptxt.pop(itmp)
if replacepair is not None:
tmpval = tmpval.replace(replacepair[0], replacepair[1])
if splitinfo[0] == 1:
tmpval = float(tmpval.split(splitinfo[1])[splitinfo[2]])
elif splitinfo[0] == 2:
tmpval = float(tmpval.split(splitinfo[1])[splitinfo[2]].split()[splitinfo[3]])
else:
raise ValueError('splitinfo[0] has to be either 1 or 2')
res.append(tmpval)
res = array(res)
return res
[docs]def get_rms(outfile, outfile2, debug=False, is_imp_calc=False):
"""
Get rms error per atom (both values for charge and spin) and total (i.e. average) value
"""
if debug:
print((outfile, outfile2))
rms_charge = parse_array_float(outfile, 'average rms-error', [2, '=', 1, 0], ['D', 'E'], debug=debug)
if debug:
print(rms_charge)
if is_imp_calc:
rms_ldau = parse_array_float(outfile, 'TOTAL RMS-ERROR for LDA+U:', [2, ':', 1, 0], debug=debug)
if debug:
print(rms_ldau)
rms_spin = parse_array_float(
outfile, ' v+ - v-', [1, '=', 1],
['D', 'E']) # this should be in the line after 'average rms-error' but is only present if NSPIN==2
if debug:
print(rms_spin)
rms_charge_atoms = parse_array_float(outfile2, 'rms-error for atom', [2, '=', 1, 0], ['D', 'E'])
if debug:
print(rms_charge_atoms)
rms_spin_atoms = parse_array_float(outfile2, 'rms-error for atom', [2, '=', 1, 0],
['D', 'E']) # only present for NSPIN==2
if debug:
print(rms_spin_atoms)
niter = len(rms_charge) # number of iterations
if debug:
print(niter)
natoms = int(len(rms_charge_atoms) //
niter) # number of atoms in system, needed to take only atom resolved rms of last iteration
if debug:
print(natoms)
if is_imp_calc:
return rms_charge, rms_ldau, rms_spin, rms_charge_atoms[-natoms:], rms_spin_atoms[-natoms:]
return rms_charge, rms_spin, rms_charge_atoms[-natoms:], rms_spin_atoms[-natoms:]
[docs]def get_noco_rms(outfile, debug=False):
"""
Get average noco rms error
"""
if debug:
print(outfile)
try:
rms_noco = parse_array_float(outfile, 'Total RMS(angles)', [1, ':', 1], debug=debug)
except: # pylint: disable=bare-except
rms_noco = []
if debug:
traceback.print_exc()
return rms_noco
def get_neutr(outfile):
res = parse_array_float(outfile, 'charge neutrality in unit cell', [1, '=', 1])
return res
def get_magtot(outfile):
res = parse_array_float(outfile, 'TOTAL mag. moment in unit cell', [1, '=', 1])
return res
def get_EF(outfile):
res = parse_array_float(outfile, 'E FERMI', [2, 'FERMI', 1, 0])
return res
def get_DOS_EF(outfile):
res = parse_array_float(outfile, 'DOS(E_F)', [1, '=', 1])
return res
def get_Etot(outfile):
res = parse_array_float(outfile, 'TOTAL ENERGY in ryd.', [1, ':', 1])
return res
def find_warnings(outfile):
tmptxt = get_outfile_txt(outfile)
tmptxt_caps = [txt.upper() for txt in tmptxt]
itmp = 0
res = []
while itmp >= 0:
itmp = search_string('WARNING', tmptxt_caps)
if itmp >= 0:
tmpval = tmptxt_caps.pop(itmp)
tmpval = tmptxt.pop(itmp)
res.append(tmpval.strip())
return array(res)
def extract_timings(outfile):
tmptxt = get_outfile_txt(outfile)
itmp = 0
res = []
search_keys = [
'main0',
'main1a - tbref',
'main1a ', # two spaces to differentiate from following key
'main1b - calctref13',
'main1b ', # two spaces!
'main1c - serial part',
'main1c ', # two spaces!
'main2',
'Time in Iteration'
]
while itmp >= 0:
tmpvals = []
for isearch in search_keys:
itmp = search_string(isearch, tmptxt)
if itmp >= 0:
tmpval = [isearch, float(tmptxt.pop(itmp).split()[-1])]
tmpvals.append(tmpval)
if len(tmpvals) > 0:
res.append(tmpvals)
res = res[0]
return dict(res)
def get_charges_per_atom(outfile_000):
res1 = parse_array_float(outfile_000, 'charge in wigner seitz', [1, '=', 1])
# these two are not in output of DOS calculation (and are then ignored)
res2 = parse_array_float(outfile_000, 'nuclear charge', [2, 'nuclear charge', 1, 0])
try:
res3 = parse_array_float(outfile_000, 'core charge', [1, '=', 1])
except IndexError:
res3 = parse_array_float(outfile_000, 'core charge', [1, ':', 1])
return res1, res2, res3
[docs]def get_single_particle_energies(outfile_000):
"""
extracts single particle energies from outfile_000 (output.000.txt)
returns the valence contribution of the single particle energies
"""
tmptxt = get_outfile_txt(outfile_000)
itmp = 0
res = []
while itmp >= 0:
itmp = search_string('band energy per atom', tmptxt)
if itmp >= 0:
tmpval = float(tmptxt.pop(itmp).split()[-1])
res.append(tmpval)
return array(res)
def get_econt_info(outfile_0init):
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('E min', tmptxt)
emin = float(tmptxt[itmp].split('min')[1].split('=')[1].split()[0])
itmp = search_string('Temperature', tmptxt)
tempr = float(tmptxt[itmp].split('Temperature')[1].split('=')[1].split()[0])
itmp = search_string('Number of energy points', tmptxt)
Nepts = int(tmptxt[itmp].split(':')[1].split()[0])
doscalc = search_string('Density-of-States calculation', tmptxt)
semi_circ = search_string('integration on semi-circle contour', tmptxt)
# dummy values
N1, N2, N3, Npol = None, None, None, None
Nsemi_circ, im_e_min = None, None
# for DOS contour
if doscalc == -1:
# scf contour
if semi_circ == -1:
# npol
itmp = search_string('poles =', tmptxt)
Npol = int(tmptxt[itmp].split('=')[1].split()[0])
# npt1, npt2, npt3
itmp = search_string('contour:', tmptxt)
tmp = tmptxt[itmp].replace(',', '').replace('=', '= ').split(':')[1].split()
N1 = int(tmp[2])
N2 = int(tmp[5])
N3 = int(tmp[8])
else:
# semi-circular contour
Nsemi_circ = Nepts
itmp = search_string('smallest imaginary part ', tmptxt)
im_e_min = tmptxt[itmp].split('=')[1].split()[0]
else:
# DOS contour
Npol, N1, N2, N3 = 0, 0, Nepts, 0
return emin, tempr, Nepts, Npol, N1, N2, N3, Nsemi_circ, im_e_min
def get_core_states(potfile):
ncore, energies, lmoments = get_corestates_from_potential(potfile=potfile)
emax, lmax, descr_max = [], [], []
for ipot, nc in enumerate(ncore):
if nc > 0:
lvalmax, energy_max, descr = get_highest_core_state(nc, energies[ipot], lmoments[ipot])
else:
lvalmax, energy_max, descr = None, None, 'no core states'
emax.append(energy_max)
lmax.append(lvalmax)
descr_max.append(descr)
return array(ncore), array(emax), array(lmax), array(descr_max)
def get_alatinfo(outfile_0init):
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('Lattice constants :', tmptxt)
alat = float(tmptxt[itmp].split(':')[1].split('=')[1].split()[0])
twopialat = float(tmptxt[itmp].split(':')[1].split('=')[2].split()[0])
return alat, twopialat
def get_scfinfo(outfile_0init, outfile_000, outfile):
tmptxt = get_outfile_txt(outfile_000)
itmp = search_string('ITERATION :', tmptxt)
tmpval = tmptxt[itmp].split(':')[1].split()
niter = int(tmpval[0])
nitermax = int(tmpval[3])
tmptxt = get_outfile_txt(outfile)
itmp1 = search_string('SCF ITERATION CONVERGED', tmptxt)
itmp2 = search_string('NUMBER OF SCF STEPS EXHAUSTED', tmptxt)
converged = itmp1 >= 0
nmax_reached = itmp2 >= 0
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('STRMIX FCM QBOUND', tmptxt)
tmpval = tmptxt[itmp + 1].split()
strmix = float(tmpval[0])
fcm = float(tmpval[1])
qbound = float(tmpval[2])
tmpval = tmptxt[itmp + 4].split()
brymix = float(tmpval[0])
itmp = search_string('IMIX IGF ICC', tmptxt)
imix = int(tmptxt[itmp + 1].split()[0])
idtbry = int(tmptxt[itmp + 4].split()[0])
mixinfo = [imix, strmix, qbound, fcm, idtbry, brymix]
return niter, nitermax, converged, nmax_reached, mixinfo
[docs]def get_kmeshinfo(outfile_0init, outfile_000):
"""
Extract kmesh info from output.0.txt and output.000.txt
"""
# first get info from output.0.txt
tmptxt = get_outfile_txt(outfile_0init)
nkmesh = []
itmp = search_string('number of different k-meshes', tmptxt)
nkmesh.append(int(tmptxt[itmp].split(':')[1].split()[0]))
itmp = search_string('NofKs', tmptxt)
nofks, nkx, nky, nkz = [], [], [], []
if itmp >= 0:
for ik in range(nkmesh[0]):
tmpval = tmptxt[itmp + 2 + ik].split()
nofks.append(int(tmpval[1]))
nkx.append(int(tmpval[2]))
nky.append(int(tmpval[3]))
nkz.append(int(tmpval[4]))
tmpdict = {'number_of_kpts': nofks, 'n_kx': nkx, 'n_ky': nky, 'n_kz': nkz}
nkmesh.append(tmpdict)
#next get kmesh_ie from output.000.txt
tmptxt = get_outfile_txt(outfile_000)
kmesh_ie = []
itmp = 0
while itmp >= 0:
itmp = search_string('KMESH =', tmptxt)
if itmp >= 0:
tmpval = int(tmptxt.pop(itmp).split()[-1])
kmesh_ie.append(tmpval)
return nkmesh, kmesh_ie
def get_symmetries(outfile_0init):
tmptxt = get_outfile_txt(outfile_0init)
try:
itmp = search_string('symmetries found for this lattice:', tmptxt)
nsym = int(tmptxt[itmp].split(':')[1].split()[0])
except IndexError:
itmp = search_string('< FINDGROUP > : Finding symmetry operations', tmptxt)
tmptxt2 = tmptxt[itmp:]
itmp = search_string('found for this lattice:', tmptxt2)
nsym = int(tmptxt2[itmp].split(':')[1].split()[0])
itmp = search_string('symmetries will be used', tmptxt)
nsym_used = int(tmptxt[itmp].split()[3])
itmp = search_string('<SYMTAUMAT>', tmptxt)
tmpdict = {}
for isym in range(nsym_used):
tmpval = tmptxt[itmp + 5 + isym].replace('0-',
'0 -').replace('1-',
'1 -').split() # bugfix for -120 degree euler angle
desc = tmpval[1]
inversion = int(tmpval[2])
euler = [float(tmpval[3]), float(tmpval[4]), float(tmpval[5])]
unitary = int(tmpval[6].replace('T', '1').replace('F', '0'))
tmpdict[desc] = {'has_inversion': inversion, 'is_unitary': unitary, 'euler_angles': euler}
desc = tmpdict
return nsym, nsym_used, desc
def get_ewald(outfile_0init):
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('setting bulk Madelung coefficients', tmptxt)
if itmp >= 0:
info = '3D'
else:
info = '2D'
if info == '3D':
itmp = search_string('< LATTICE3D >', tmptxt)
tmpval = tmptxt[itmp + 7].split()[2:]
rsum = float(tmpval[2]), int(tmpval[0]), int(tmpval[1])
tmpval = tmptxt[itmp + 8].split()[2:]
gsum = float(tmpval[2]), int(tmpval[0]), int(tmpval[1])
else:
itmp = search_string('< LATTICE2D >', tmptxt)
tmpval = tmptxt[itmp + 13].split()[2:]
rsum = float(tmpval[2]), int(tmpval[0]), int(tmpval[1])
tmpval = tmptxt[itmp + 14].split()[2:]
gsum = float(tmpval[2]), int(tmpval[0]), int(tmpval[1])
return rsum, gsum, info
[docs]def get_nspin(outfile_0init):
"""
extract NSPIN value from output.0.txt
"""
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('NSPIN', tmptxt)
nspin = int(tmptxt[itmp + 1].split()[0])
return nspin
[docs]def get_natom(outfile_0init):
"""
extract NATYP value from output.0.txt
"""
tmptxt = get_outfile_txt(outfile_0init)
itmp = search_string('NATYP', tmptxt)
natom = int(tmptxt[itmp + 1].split()[0])
return natom
[docs]def use_newsosol(outfile_0init):
"""
extract NEWSOSOL info from output.0.txt
"""
tmptxt = get_outfile_txt(outfile_0init)
newsosol = False
# old style (RUNOPT output)
itmp = search_string('NEWSOSOL', tmptxt)
if itmp >= 0 and 'NEWSOSOL' in tmptxt[itmp].upper():
newsosol = True
itmp = search_string('<use_Chebychev_solver>=', tmptxt)
# new style: check for output of runoptions
if itmp >= 0:
if tmptxt[itmp].split()[1][:1].upper() == 'T':
newsosol = True
if tmptxt[itmp].split()[1][:1].upper() == 'F':
newsosol = False
return newsosol
[docs]def use_BdG(outfile_0init):
"""
extract BdG run info from output.0.txt
"""
tmptxt = get_outfile_txt(outfile_0init)
val_use_BdG = False
itmp = search_string('<use_BdG>=', tmptxt)
if itmp >= 0:
if tmptxt[itmp].split()[1][:1].upper() == 'T':
val_use_BdG = True
if tmptxt[itmp].split()[1][:1].upper() == 'F':
val_use_BdG = False
return val_use_BdG
[docs]def get_spinmom_per_atom(outfile, natom, nonco_out_file=None):
"""
Extract spin moment information from outfile and nonco_angles_out (if given)
"""
tmptxt = get_outfile_txt(outfile)
itmp = 0
result = []
while itmp >= 0:
itmp = search_string('m_spin', tmptxt)
if itmp >= 0:
tmpline = tmptxt.pop(itmp)
tmparray = []
for iatom in range(natom):
tmpline = tmptxt.pop(itmp)
tmparray.append(float(tmpline.split()[3]))
result.append(tmparray)
# if the file is there, i.e. NEWSOSOL is used, then extract also direction of spins (angles theta and phi)
if nonco_out_file is not None and result:
angles = loadtxt(nonco_out_file, usecols=[0, 1]) # make sure only theta and phi are read in
if len(shape(angles)) == 1:
angles = array([angles])
vec = angles_to_vec(result[-1], angles[:, 0] / 180. * np.pi, angles[:, 1] / 180. * np.pi)
else:
vec, angles = [], []
return array(result), vec, angles
[docs]def get_orbmom(outfile, natom):
"""
read orbmom info from outfile and return array (iteration, atom)=orbmom
"""
tmptxt = get_outfile_txt(outfile)
itmp = 0
result = []
while itmp >= 0:
itmp = search_string('m_spin', tmptxt)
if itmp >= 0:
tmpline = tmptxt.pop(itmp)
tmparray = []
for iatom in range(natom):
tmpline = tmptxt.pop(itmp)
tmparray.append(float(tmpline.split()[4]))
result.append(tmparray)
return array(result) #, vec, angles
[docs]def get_lattice_vectors(outfile_0init):
"""
read direct and reciprocal lattice vectors in internal units (useful for qdos generation)
"""
tmptxt = get_outfile_txt(outfile_0init)
vecs, rvecs = [], []
tmpvecs = []
for search_txt in ['a_1: ', 'a_2: ', 'a_3: ', 'b_1: ', 'b_2: ', 'b_3: ']:
itmp = search_string(search_txt, tmptxt)
if itmp >= 0:
tmpvec = tmptxt[itmp].split(':')[1].split()
tmpvecs.append([float(tmpvec[0]), float(tmpvec[1]), float(tmpvec[1])])
if search_txt in ['a_3: ', 'b_3: '] and itmp < 0:
# reset vecs for 2D case
tmpvecs[0] = tmpvecs[0][:2]
tmpvecs[1] = tmpvecs[1][:2]
if search_txt == 'a_3: ':
vecs = tmpvecs
tmpvecs = []
elif search_txt == 'b_3: ':
rvecs = tmpvecs
return vecs, rvecs
[docs]def parse_kkr_outputfile(out_dict,
outfile,
outfile_0init,
outfile_000,
timing_file,
potfile_out,
nonco_out_file,
outfile_2='output.2.txt',
skip_readin=False,
debug=False):
"""
Parser method for the kkr outfile. It returns a dictionary with results
"""
# scaling factors etc. defined globally
doscalc = False
# collection of parsing error messages
msg_list = []
try:
code_version, compile_options, serial_number = get_version_info(outfile)
tmp_dict = {}
tmp_dict['code_version'] = code_version
tmp_dict['compile_options'] = compile_options
tmp_dict['calculation_serial_number'] = serial_number
out_dict['code_info_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: Version Info'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
nspin = get_nspin(outfile_0init)
natom = get_natom(outfile_0init)
newsosol = use_newsosol(outfile_0init)
out_dict['nspin'] = nspin
out_dict['number_of_atoms_in_unit_cell'] = natom
out_dict['use_newsosol'] = newsosol
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: nspin/natom'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
# extract some BdG infos
out_dict['use_BdG'] = use_BdG(outfile_0init)
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: BdG'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = find_warnings(outfile)
tmp_dict = {}
tmp_dict['number_of_warnings'] = len(result)
tmp_dict['warnings_list'] = result
out_dict['warnings_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: search for warnings'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = extract_timings(timing_file)
out_dict['timings_group'] = result
out_dict['timings_unit'] = 'seconds'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: timings'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
emin, tempr, Nepts, Npol, N1, N2, N3, Nsemi_circ, im_e_min = get_econt_info(outfile_0init)
tmp_dict = {}
tmp_dict['emin'] = emin
tmp_dict['emin_unit'] = 'Rydberg'
tmp_dict['number_of_energy_points'] = Nepts
if Nsemi_circ is None:
# normal scf or DOS contour
tmp_dict['temperature'] = tempr
tmp_dict['temperature_unit'] = 'Kelvin'
tmp_dict['npol'] = Npol
tmp_dict['n1'] = N1
tmp_dict['n2'] = N2
tmp_dict['n3'] = N3
else:
# semi-circle contour
tmp_dict['im_e_min'] = im_e_min
tmp_dict['im_e_min_unit'] = 'Ry'
# now fill energy contour group
out_dict['energy_contour_group'] = tmp_dict
if Npol == 0:
doscalc = True
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: energy contour'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
alat, twopioveralat = get_alatinfo(outfile_0init)
out_dict['alat_internal'] = alat
out_dict['two_pi_over_alat_internal'] = twopioveralat
out_dict['alat_internal_unit'] = 'a_Bohr'
out_dict['two_pi_over_alat_internal_unit'] = '1/a_Bohr'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: alat, 2*pi/alat'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
nkmesh, kmesh_ie = get_kmeshinfo(outfile_0init, outfile_000)
tmp_dict = {}
tmp_dict['number_different_kmeshes'] = nkmesh[0]
tmp_dict['number_kpoints_per_kmesh'] = nkmesh[1]
tmp_dict['kmesh_energypoint'] = kmesh_ie
out_dict['kmesh_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: kmesh'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
nsym, nsym_used, desc = get_symmetries(outfile_0init)
tmp_dict = {}
tmp_dict['number_of_lattice_symmetries'] = nsym
tmp_dict['number_of_used_symmetries'] = nsym_used
tmp_dict['symmetry_description'] = desc
out_dict['symmetries_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: symmetries'
msg_list.append(msg)
if debug:
traceback.print_exc()
if not doscalc: # in case of dos calculation no ewald summation is done
try:
rsum, gsum, info = get_ewald(outfile_0init)
tmp_dict = {}
tmp_dict['ewald_summation_mode'] = info
tmp_dict['rsum_cutoff'] = rsum[0]
tmp_dict['rsum_number_of_vectors'] = rsum[1]
tmp_dict['rsum_number_of_shells'] = rsum[2]
tmp_dict['rsum_cutoff_unit'] = 'a_Bohr'
tmp_dict['gsum_cutoff'] = gsum[0]
tmp_dict['gsum_number_of_vectors'] = gsum[1]
tmp_dict['gsum_number_of_shells'] = gsum[2]
tmp_dict['gsum_cutoff_unit'] = '1/a_Bohr'
out_dict['ewald_sum_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: ewald summation for madelung poterntial'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
bv, recbv = get_lattice_vectors(outfile_0init)
out_dict['direct_bravais_matrix'] = bv
out_dict['reciprocal_bravais_matrix'] = recbv
out_dict['direct_bravais_matrix_unit'] = 'alat'
out_dict['reciprocal_bravais_matrix_unit'] = '2*pi / alat'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: lattice vectors (direct/reciprocal)'
msg_list.append(msg)
if debug:
traceback.print_exc()
# this is skipped for qdos run for example
if not skip_readin and not doscalc:
try:
ncore, emax, lmax, descr_max = get_core_states(potfile_out)
tmp_dict = {}
tmp_dict['number_of_core_states_per_atom'] = ncore
tmp_dict['energy_highest_lying_core_state_per_atom'] = emax
tmp_dict['energy_highest_lying_core_state_per_atom_unit'] = 'Rydberg'
tmp_dict['descr_highest_lying_core_state_per_atom'] = descr_max
out_dict['core_states_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: core_states'
msg_list.append(msg)
if debug:
traceback.print_exc()
tmp_dict = {} # used to group convergence info (rms, rms per atom, charge neutrality)
# also initialize convegence_group where all info stored for all iterations is kept
out_dict['convergence_group'] = tmp_dict
try:
rms_charge, rms_spin, result_atoms_last_charge, result_atoms_last_spin = get_rms( # pylint: disable=unbalanced-tuple-unpacking
outfile, outfile_000, debug=debug)
tmp_dict['rms'] = rms_charge[-1]
tmp_dict['rms_all_iterations'] = rms_charge
tmp_dict['rms_per_atom'] = result_atoms_last_charge
if len(rms_spin) > 0:
tmp_dict['rms_spin'] = rms_spin[-1]
else:
tmp_dict['rms_spin'] = None
tmp_dict['rms_spin_all_iterations'] = rms_spin
tmp_dict['rms_spin_per_atom'] = result_atoms_last_spin
tmp_dict['rms_unit'] = 'unitless'
out_dict['convergence_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: rms-error'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = get_neutr(outfile)
tmp_dict['charge_neutrality'] = result[-1]
out_dict['convergence_group']['charge_neutrality_all_iterations'] = result
tmp_dict['charge_neutrality_unit'] = 'electrons'
out_dict['convergence_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: charge neutrality'
msg_list.append(msg)
if debug:
traceback.print_exc()
tmp_dict = {} # used to group magnetism info (spin and orbital moments)
try:
result = get_magtot(outfile)
if len(result) > 0:
tmp_dict['total_spin_moment'] = result[-1]
out_dict['convergence_group']['total_spin_moment_all_iterations'] = result
tmp_dict['total_spin_moment_unit'] = 'mu_Bohr'
out_dict['magnetism_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: total magnetic moment'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
if nspin > 1:
if not newsosol:
#reset automatically to None to turn off reading of nonco angles file
nonco_out_file = None
result, vec, angles = get_spinmom_per_atom(outfile, natom, nonco_out_file)
if len(result) > 0:
tmp_dict['spin_moment_per_atom'] = result[-1, :]
if newsosol:
tmp_dict['spin_moment_vector_per_atom'] = vec[:]
tmp_dict['spin_moment_angles_per_atom'] = angles[:]
tmp_dict['spin_moment_angles_per_atom_unit'] = 'degree'
out_dict['convergence_group']['spin_moment_per_atom_all_iterations'] = result[:, :]
tmp_dict['spin_moment_unit'] = 'mu_Bohr'
out_dict['magnetism_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: spin moment per atom'
msg_list.append(msg)
if debug:
traceback.print_exc()
# add orbital moments to magnetis group in parser output
try:
if nspin > 1 and newsosol:
#TODO orbital moment full vectors
# so far the KKR code writes only the component of the orbital moment
# parallel to the spin moment, thus vec and angles are returned empty
# by construction. This might change in the future
#result, vec, angles = get_orbmom(outfile, natom, nonco_angles_orbmom)
# so for now return only result= array containing all iterations, all atoms, orbital moment parallel to spin quantization axis
result = get_orbmom(outfile, natom)
if len(result) > 0:
tmp_dict['total_orbital_moment'] = sum(result[-1, :])
tmp_dict['orbital_moment_per_atom'] = result[-1, :]
#tmp_dict['orbital_moment_vector_per_atom'] = vec[-1,:]
#tmp_dict['orbital_moment_angles_per_atom'] = angles[-1,:]
out_dict['convergence_group']['orbital_moment_per_atom_all_iterations'] = result[:, :]
tmp_dict['orbital_moment_unit'] = 'mu_Bohr'
#tmp_dict['orbital_moment_angles_per_atom_unit'] = 'degree'
out_dict['magnetism_group'] = tmp_dict
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: orbital moment'
msg_list.append(msg)
if debug:
traceback.print_exc()
# get RMS info for nonco angles
try:
if nspin > 1 and newsosol:
result = get_noco_rms(outfile, debug)
if len(result) > 0:
out_dict['convergence_group']['noco_angles_rms_all_iterations'] = result[:]
out_dict['convergence_group']['noco_angles_rms_all_iterations_unit'] = 'degrees'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: noco angles rms value'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = get_EF(outfile)
out_dict['fermi_energy'] = result[-1]
out_dict['fermi_energy_units'] = 'Ry'
out_dict['convergence_group']['fermi_energy_all_iterations'] = result
out_dict['convergence_group']['fermi_energy_all_iterations_units'] = 'Ry'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: EF'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = get_DOS_EF(outfile)
out_dict['dos_at_fermi_energy'] = result[-1]
out_dict['convergence_group']['dos_at_fermi_energy_all_iterations'] = result
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: DOS@EF'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = get_Etot(outfile)
out_dict['energy'] = result[-1] * get_Ry2eV()
out_dict['energy_unit'] = 'eV'
out_dict['total_energy_Ry'] = result[-1]
out_dict['total_energy_Ry_unit'] = 'Rydberg'
out_dict['convergence_group']['total_energy_Ry_all_iterations'] = result
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: total energy'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result = get_single_particle_energies(outfile_000)
out_dict['single_particle_energies'] = result * get_Ry2eV()
out_dict['single_particle_energies_unit'] = 'eV'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: single particle energies'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
result_WS, result_tot, result_C = get_charges_per_atom(outfile_000)
niter = len(out_dict['convergence_group']['rms_all_iterations'])
natyp = int(len(result_tot) // niter)
out_dict['total_charge_per_atom'] = result_tot[-natyp:]
out_dict['charge_core_states_per_atom'] = result_C[-natyp:]
# this check deals with the DOS case where output is slightly different
if len(result_WS) == len(result_C):
out_dict['charge_valence_states_per_atom'] = result_WS[-natyp:] - result_C[-natyp:]
out_dict['total_charge_per_atom_unit'] = 'electron charge'
out_dict['charge_core_states_per_atom_unit'] = 'electron charge'
out_dict['charge_valence_states_per_atom_unit'] = 'electron charge'
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: charges'
msg_list.append(msg)
if debug:
traceback.print_exc()
try:
try:
niter, nitermax, converged, nmax_reached, mixinfo = get_scfinfo(outfile_0init, outfile_000, outfile)
except IndexError:
niter, nitermax, converged, nmax_reached, mixinfo = get_scfinfo(outfile_0init, outfile_2, outfile)
out_dict['convergence_group']['number_of_iterations'] = niter
out_dict['convergence_group']['number_of_iterations_max'] = nitermax
out_dict['convergence_group']['calculation_converged'] = converged
out_dict['convergence_group']['nsteps_exhausted'] = nmax_reached
out_dict['convergence_group']['imix'] = mixinfo[0]
out_dict['convergence_group']['strmix'] = mixinfo[1]
out_dict['convergence_group']['qbound'] = mixinfo[2]
out_dict['convergence_group']['fcm'] = mixinfo[3]
out_dict['convergence_group']['idtbry'] = mixinfo[4]
out_dict['convergence_group']['brymix'] = mixinfo[5]
except: # pylint: disable=bare-except
msg = 'Error parsing output of KKR: scfinfo'
msg_list.append(msg)
if debug:
traceback.print_exc()
#convert numpy arrays to standard python lists
out_dict = convert_to_pystd(out_dict)
# return output with error messages if there are any
return len(msg_list) == 0, msg_list, out_dict
[docs]def check_error_category(err_cat, err_msg, out_dict):
"""
Check if parser error of the non-critical category (err_cat != 1) are
actually consistent and may be discarded.
:param err_cat: the error-category of the error message to be investigated
:param err_msg: the error-message
:param out_dict: the dict of results obtained from the parser function
:returns: True/False if message is an error or warning
"""
# check special cases:
# 1. nonco_angle_file not present, but newsosol==False anyways
if 'NONCO_ANGLES_OUT' in err_msg:
return out_dict.get('use_newsosol', True)
# default behavior
return err_cat == 1