Source code for masci_tools.io.parsers.kkrimp_parser_functions

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#use print('message') instead of print 'message' in python 2.7 as well:
from __future__ import print_function

"""
Tools for the impurity caluclation plugin and its workflows
"""
         
__copyright__ = (u"Copyright (c), 2018, Forschungszentrum Jülich GmbH,"
                 "IAS-1/PGI-1, Germany. All rights reserved.")
__license__ = "MIT license, see LICENSE.txt file"
__version__ = "0.3"
__contributors__ = (u"Philipp Rüßmann", 
                    u"Fabian Bertoldo")




[docs]class modify_potential(): """ Class for old modify potential script, ported from modify_potential script, initially by D. Bauer """ def _check_potstart(self, str1, mode='pot', shape_ver='new'): if mode=='shape': if shape_ver=='new': check1='Shape number' in str1 else: check1= (len(str1)==11) else: check1='exc:' in str1 return check1 def _read_input(self, filepath): #print(filepath) with open(filepath) as file: data = file.readlines() if 'shapefun' in filepath: mode = 'shape' else: mode = 'pot' #print(mode, len(data)) # read file index1=[];index2=[] for i in range(len(data)): if self._check_potstart(data[i], mode=mode): index1.append(i) if len(index1)>1: index2.append(i-1) index2.append(i) # read shapefun if old style is used if mode=='shape' and len(index1)<1: index1=[];index2=[] for i in range(len(data)): if self._check_potstart(data[i], mode=mode, shape_ver='old'): index1.append(i) if len(index1)>1: index2.append(i-1) index2.append(i) """ print(index1) print(index2) print('Potential file read') print('found %i potentials in file'%len(index1)) print('') """ return index1, index2, data
[docs] def shapefun_from_scoef(self, scoefpath, shapefun_path, atom2shapes, shapefun_new): """ Read shapefun and create impurity shapefun using scoef info and shapes array :param scoefpath: absolute path to scoef file :param shapefun_path: absolute path to input shapefun file :param shapes: shapes array for mapping between atom index and shapefunction index :param shapefun_new: absolute path to output shapefun file to which the new shapefunction will be written """ index1, index2, data = self._read_input(shapefun_path) order=range(len(index1)) natomtemp = int(open(scoefpath).readlines()[0]) filedata=open(scoefpath).readlines()[1:natomtemp+1] listnew=[] for line in filedata: if (len(line.split())>1): listnew.append(atom2shapes[int(line.split()[3])-1]-1) order = listnew datanew=[] for i in range(len(order)): for ii in range(index1[order[i]], index2[order[i]]+1 ): datanew.append(data[ii]) # add header to shapefun_new tmp = datanew datanew = [] datanew.append(' %i\n' %(len(order))) datanew.append(' 1.000000000000E+00\n') datanew += tmp open(shapefun_new,'w').writelines(datanew)
[docs] def neworder_potential(self, potfile_in, potfile_out, neworder, potfile_2=None, replace_from_pot2=None): """ Read potential file and new potential using a list describing the order of the new potential. If a second potential is given as input together with an index list, then the corresponding of the output potential are overwritten with positions from the second input potential. :param potfile_in: absolute path to input potential :type potfile_in: str :param potfile_out: absolute path to output potential :type potfile_out: str :param neworder: list after which output potential is constructed from input potential :type neworder: list :param potfile_2: optional, absolute path to second potential file if positions in new list of potentials shall be replaced by positions of second potential, requires *replace_from_pot* to be given as well :type potfile_2: str :param replace_from_pot: optional, list containing tuples of (position in newlist that is to be replaced, position in pot2 with which position is replaced) :type replace_from_pot: list :usage: 1. modify_potential().neworder_potential(<path_to_input_pot>, <path_to_output_pot>, []) """ from numpy import array, shape index1, index2, data = self._read_input(potfile_in) if potfile_2 is not None: index12, index22, data2 = self._read_input(potfile_2) # check if also replace_from_pot2 is given correctly if replace_from_pot2 is None: raise ValueError('replace_from_pot2 not given') else: replace_from_pot2 = array(replace_from_pot2) if shape(replace_from_pot2)[1]!=2: raise ValueError('replace_from_pot2 needs to be a 2D array!') else: if replace_from_pot2 is not None: raise ValueError('replace_from_pot2 given but potfile_2 not given') # set order in which potential file is written # ensure that numbers are integers: order = [int(i) for i in neworder] datanew=[] for i in range(len(order)): # check if new position is replaced with position from old pot if replace_from_pot2 is not None and i in replace_from_pot2[:,0]: replace_index = replace_from_pot2[replace_from_pot2[:,0]==i][0][1] for ii in range(index12[replace_index], index22[replace_index]+1 ): datanew.append(data2[ii]) else: # otherwise take new potntial according to input list for ii in range(index1[order[i]], index2[order[i]]+1 ): datanew.append(data[ii]) # write out new potential open(potfile_out,'w').writelines(datanew)
[docs]class kkrimp_parser_functions(): """ Class of parser functions for KKRimp calculation :usage: success, msg_list, out_dict = parse_kkrimp_outputfile().parse_kkrimp_outputfile(out_dict, files) """ ### some helper functions ### def _get_econt_info(self, out_log): """ extract energy contour information from out_log file :param out_log: file that is parsed :retuns: econt (dict), dictionary containing the energy contour info :note: econt contains the following keys * 'emin', bottom of energy contour * 'Nepts', number of points in energy contour * 'epts', list of complex valued energy points * 'weights', list of complex valued weights for energy integration """ from masci_tools.io.common_functions import search_string from numpy import array f = open(out_log) tmptxt = f.readlines() f.close() econt = {} itmp = search_string('[read_energy] number of energy points', tmptxt) if itmp>=0: econt['Nepts'] = int(tmptxt.pop(itmp).split()[-1]) itmp = search_string('energies and weights are:', tmptxt) if itmp>=0: tmp = [] for ie in range(econt['Nepts']): tmpline = tmptxt[itmp+4+ie].split()[1:] tmp.append([float(tmpline[0]), float(tmpline[1]), float(tmpline[2]), float(tmpline[3])]) tmp = array(tmp) econt['epts'] = tmp[:,:2] econt['weights'] = tmp[:,2:] econt['emin'] = tmp[0,0] return econt def _get_scfinfo(self, file): """ extract scf infos (nunmber of iterations, max number of iterations, mixing info) from file :param file: :returns: niter (int), nitermax (int), converged (bool), nmax_reached (bool), mixinfo (dict) :note: mixinfo contains information on mixing scheme and mixing factor used in the calculation """ from masci_tools.io.common_functions import search_string f = open(file) tmptxt = f.readlines() f.close() # get rms and number of iterations itmp, niter, rms = 0, -1, -1 while itmp >= 0: itmp = search_string('average rms-error', tmptxt) if itmp >= 0: tmp = tmptxt.pop(itmp).replace('D', 'E').split() niter = int(tmp[1]) rms = float(tmp[-1]) # get max number of scf steps itmp = search_string('SCFSTEPS', tmptxt) if itmp >= 0: nitermax = int(tmptxt.pop(itmp).split()[-1]) # get qbound itmp = search_string('QBOUND', tmptxt) if itmp >= 0: qbound = float(tmptxt.pop(itmp).split()[-1]) # get imix itmp = search_string('IMIX', tmptxt) if itmp >= 0: imix = int(tmptxt.pop(itmp).split()[-1]) # get mixfac itmp = search_string('MIXFAC', tmptxt) if itmp >= 0: mixfac = float(tmptxt.pop(itmp).split()[-1]) # get fcm itmp = search_string('FCM', tmptxt) if itmp >= 0: fcm = float(tmptxt.pop(itmp).split()[-1]) # set mixinfo mixinfo = [imix, mixfac, qbound, fcm] # set converged and nmax_reached logicals converged, nmax_reached = False, False if nitermax==niter: nmax_reached = True if rms<qbound: converged = True # return values return niter, nitermax, converged, nmax_reached, mixinfo def _get_newsosol(self, file): """ Check if spin orbit coupling solver is used :param file: absolute path to out_log.000.txt of KKRimp calculation :returns: True(False) if SOC solver is (not) used """ from masci_tools.io.common_functions import search_string f = open(file) tmptxt = f.readlines() f.close() itmp = search_string('Spin orbit coupling used?', tmptxt) itmp = int(tmptxt.pop(itmp).split()[-1]) if itmp==1: newsosol = True else: newsosol = False return newsosol def _get_natom(self, file): """ Extract number of atoms in impurity cluster :param file: file that is parsed to find number of atoms :returns: natom (int), number of atoms in impurity cluster """ from masci_tools.io.common_functions import search_string f = open(file) tmptxt = f.readlines() f.close() itmp = search_string('NATOM is', tmptxt) natom = int(tmptxt.pop(itmp).split()[-1]) return natom def _get_magtot(self, file): """ Extract total magnetic moment ofall atoms in imp. cluster :param file: file that is parsed to find magnetic moments :returns: list of total magnetic moments of all atoms """ #TODO implement return [] def _extract_timings(self, outfile): """ Extract timings for the different parts in the KKRimp code :param outfile: timing file of the KKRimp run :returns: res (dict) timings in seconds, averaged over iterations """ from masci_tools.io.common_functions import search_string f = open(outfile) tmptxt = f.readlines() f.close() search_keys = ['time until scf starts', 'vpot->tmat', 'gref->gmat', 'gonsite->density', 'energyloop', 'Iteration number', 'Total running time'] res = {} for isearch in search_keys: tmpval = [] itmp = 0 while itmp>=0: itmp = search_string(isearch, tmptxt) if itmp>=0: tmpval.append(float(tmptxt.pop(itmp).split()[-1])) if len(tmpval)>0: res[isearch] = tmpval # average over iterations niter = len(res.get(search_keys[-2], [])) if niter>0: for key in search_keys[1:6]: res[key] = sum(res[key])/niter for key in [search_keys[0], search_keys[-1]]: res[key] = res[key][0] return res def _get_nspin(self, file): """ Extract nspin from file :param file: file that is parsed :returns: 1 if calculation is paramagnetic, 2 otherwise """ from masci_tools.io.common_functions import search_string f = open(file) tmptxt = f.readlines() f.close() itmp = search_string('NSPIN', tmptxt) nspin = int(tmptxt.pop(itmp).split()[-1]) return nspin def _get_spinmom_per_atom(self, file, natom): """ Extract spin moment for all atoms :param file: file that is parsed :param natom: number of atoms in impurity cluster :returns: spinmom_at (list), spin moments for all atoms """ #TODO implement return spinmom_at def _get_orbmom_per_atom(self, file, natom): """ Extract orbital moment for all atoms :param file: file that is parsed :param natom: number of atoms in impurity cluster :returns: orbmom_at (list), orbital moments for all atoms """ #TODO implement return orbmom_at def _get_EF_potfile(self, potfile): """ Extract EF value from potential file :param potfile: file that is parsed :returns: EF (float), value of the Fermi energy in Ry """ f = open(potfile) tmptxt = f.readlines() f.close() EF = float(tmptxt[3].split()[1]) return EF def _get_Etot(self, file): """ Extract total energy file :param file: file that is parsed :returns: Etot (list), values of the total energy in Ry for all iterations """ from masci_tools.io.common_functions import search_string f = open(file) tmptxt = f.readlines() f.close() itmp = 0 Etot = [] while itmp >= 0: itmp = search_string('TOTAL ENERGY', tmptxt) if itmp >= 0: Etot.append(float(tmptxt.pop(itmp).split()[-1])) return Etot def _get_energies_atom(self, file1, file2, natom): """ Extract single particle and total energies in Ry for all atoms from file 1 and file 2 :param file1: file containing all single particle energies :param file2: file containing all total energies :returns: esp_at (list), etot_at (list) """ from numpy import loadtxt esp = loadtxt(file1) etot = loadtxt(file2) esp_at = esp[-natom:,1] etot_at = etot[-natom:,1] return esp_at, etot_at ### end helper functions ###
[docs] def parse_kkrimp_outputfile(self, out_dict, file_dict): """ Main parser function for kkrimp, read information from files in file_dict and fills out_dict :param out_dict: dictionary that is filled with parsed output of the KKRimp calculation :param file_dict: dictionary of files that are parsed :returns: success (bool), msg_list(list of error/warning messages of parser), out_dict (filled dict of parsed output) :note: file_dict should contain the following keys * 'outfile', the std_out of the KKRimp calculation * 'out_log', the out_log.000.txt file * 'out_pot', the output potential * 'out_enersp_at', the out_energysp_per_atom_eV file * 'out_enertot_at', the out_energytotal_per_atom_eV file * 'out_timing', the timing file * 'kkrflex_llyfac', the file for the Lloyd factor * 'kkrflex_angles', the nonco_angles file for the KKRimp calculation * 'out_spinmoms', the output spin moments file * 'out_orbmoms', the output orbital moments file """ from masci_tools.io.parsers.kkrparser_functions import get_rms, find_warnings, get_charges_per_atom, get_core_states from masci_tools.io.common_functions import get_version_info, get_Ry2eV Ry2eV = get_Ry2eV() msg_list = [] files = file_dict try: code_version, compile_options, serial_number = get_version_info(files['out_log']) 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: msg = "Error parsing output of KKRimp: Version Info" msg_list.append(msg) 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: result, result_atoms_last = get_rms(files['outfile'], files['out_log']) tmp_dict['rms'] = result[-1] tmp_dict['rms_all_iterations'] = result tmp_dict['rms_per_atom'] = result_atoms_last tmp_dict['rms_unit'] = 'unitless' out_dict['convergence_group'] = tmp_dict except: msg = "Error parsing output of KKRimp: rms-error" msg_list.append(msg) tmp_dict = {} # used to group magnetism info (spin and orbital moments) try: result = self._get_magtot(files['out_log']) 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: msg = "Error parsing output of KKRimp: total magnetic moment" msg_list.append(msg) try: nspin = self._get_nspin(files['out_log']) natom = self._get_natom(files['out_log']) newsosol = self._get_newsosol(files['out_log']) out_dict['nspin'] = nspin out_dict['number_of_atoms_in_unit_cell'] = natom out_dict['use_newsosol'] = newsosol except: msg = "Error parsing output of KKRimp: nspin/natom" msg_list.append(msg) try: if nspin>1: #result, vec, angles = get_spinmom_per_atom(outfile, natom, nonco_out_file) spinmom_atom, spinmom_atom_vec_all_iter, = self._get_spinmom_per_atom(files['out_spinmom'], natom) 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: msg = "Error parsing output of KKRimp: spin moment per atom" msg_list.append(msg) # add orbital moments to magnetis group in parser output try: if nspin>1 and newsosol: orbmom_atom = self._get_orbmom_per_atom(files['out_orbmom'], natom) if len(result)>0: tmp_dict['total_orbital_moment'] = sum(result[-1,:]) tmp_dict['orbital_moment_per_atom'] = result[-1,:] out_dict['convergence_group']['orbital_moment_per_atom_all_iterations'] = result[:,:] tmp_dict['orbital_moment_unit'] = 'mu_Bohr' out_dict['magnetism_group'] = tmp_dict except: msg = "Error parsing output of KKRimp: orbital moment" msg_list.append(msg) try: result = self._get_EF_potfile(files['out_pot']) out_dict['fermi_energy'] = result out_dict['fermi_energy_units'] = 'Ry' except: msg = "Error parsing output of KKRimp: EF" msg_list.append(msg) try: result = self._get_Etot(files['out_log']) print(result) out_dict['energy'] = result[-1]*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: msg = "Error parsing output of KKRimp: total energy" msg_list.append(msg) try: result = find_warnings(files['outfile']) tmp_dict = {} tmp_dict['number_of_warnings'] = len(result) tmp_dict['warnings_list'] = result out_dict['warnings_group'] = tmp_dict except: msg = "Error parsing output of KKRimp: search for warnings" msg_list.append(msg) try: result = self._extract_timings(files['out_timing']) out_dict['timings_group'] = result out_dict['timings_unit'] = 'seconds' except: msg = "Error parsing output of KKRimp: timings" msg_list.append(msg) try: esp_at, etot_at = self._get_energies_atom(files['out_enersp_at'], files['out_enertot_at'], natom) out_dict['single_particle_energies'] = esp_at*Ry2eV out_dict['single_particle_energies_unit'] = 'eV' out_dict['total_energies_atom'] = etot_at*Ry2eV out_dict['total_energies_atom_unit'] = 'eV' except: msg = "Error parsing output of KKRimp: single particle energies" msg_list.append(msg) try: result_WS, result_tot, result_C = get_charges_per_atom(files['out_log']) niter = len(out_dict['convergence_group']['rms_all_iterations']) natyp = int(len(result_tot)/niter) out_dict['total_charge_per_atom'] = result_WS[-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: msg = "Error parsing output of KKRimp: charges" msg_list.append(msg) try: econt = self._get_econt_info(files['out_log']) tmp_dict = {} tmp_dict['emin'] = econt.get('emin') tmp_dict['emin_unit'] = 'Rydberg' tmp_dict['number_of_energy_points'] = econt.get('Nepts') tmp_dict['epoints_contour'] = econt.get('epts') tmp_dict['epoints_contour_unit'] = 'Rydberg' tmp_dict['epoints_weights'] = econt.get('weights') out_dict['energy_contour_group'] = tmp_dict except: msg = "Error parsing output of KKRimp: energy contour" msg_list.append(msg) try: ncore, emax, lmax, descr_max = get_core_states(files['out_pot']) 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: msg = "Error parsing output of KKRimp: core_states" msg_list.append(msg) try: niter, nitermax, converged, nmax_reached, mixinfo = self._get_scfinfo(files['out_log']) 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']['brymix'] = mixinfo[1] except: msg = "Error parsing output of KKRimp: scfinfo" msg_list.append(msg) #convert arrays to lists from numpy import ndarray for key in out_dict.keys(): if type(out_dict[key])==ndarray: out_dict[key] = list(out_dict[key]) elif type(out_dict[key])==dict: for subkey in out_dict[key].keys(): if type(out_dict[key][subkey])==ndarray: out_dict[key][subkey] = (out_dict[key][subkey]).tolist() # return output with error messages if there are any if len(msg_list)>0: return False, msg_list, out_dict else: return True, [], out_dict