Source code for pyprocar.utilsprocar.utilsprocar

import logging
import re
import sys

import matplotlib.pyplot as plt
import numpy as np


[docs]class UtilsProcar: """ This class store handy methods that do not fit any other place members: -Openfile: Tries to open a File, it has suitable values for PROCARs and can handle gzipped files -MergeFiles: concatenate two or more PROCAR files taking care of metadata and kpoint indexes. Useful for splitted bandstructures calculation. -FermiOutcar: it greps the Fermi Energy from a given outcar file. -RecLatOutcar: it greps the reciprocal lattice from the outcar. """ def __init__(self, loglevel=logging.WARNING): self.log = logging.getLogger("UtilsProcar") self.log.setLevel(loglevel) self.ch = logging.StreamHandler() self.ch.setFormatter( logging.Formatter("%(name)s::%(levelname)s:" " %(message)s") ) self.ch.setLevel(logging.DEBUG) self.log.addHandler(self.ch) self.log.debug("UtilsProcar()") self.log.debug("UtilsProcar()...done") return ###############################SCRIPTS#################################################################### # #calls ProcarRepair # def scriptRepair(self,infile,outfile): # print "Input File : ", infile # print "Output File : ", outfile # #parsing the file # handler = UtilsProcar() # handler.ProcarRepair(infile,outfile) # calls MergeFiles # inFiles should be a list of the PROCAR files that require concatenation # def scriptCat(self,inFiles,outFile,gz=False): # print "Concatenating:" # print "Input : ", ', '.join(inFiles) # print "Output : ", outFile # if gz==True: # print "out compressed: True" # if gz=="True" and outFile[-3:] is not '.gz': # outFile += '.gz' # print ".gz extension appended to the outFile" # handler = UtilsProcar() # handler.MergeFiles(inFiles,outFile, gzipOut=gz) # return ####################################################################################################################################################
[docs] def OpenFile(self, FileName=None): """ Tries to open a File, it has suitable values for PROCAR and can handle gzipped files Example: >>> foo = UtilsProcar.Openfile() Tries to open "PROCAR", then "PROCAR.gz" >>> foo = UtilsProcar.Openfile("../bar") Tries to open "../bar". If it is a directory, it will try to open "../bar/PROCAR" and if fails again "../bar/PROCAR.gz" >>> foo = UtilsProcar.Openfile("PROCAR-spd.gz") Tries to open a gzipped file "PROCAR-spd.gz" If unable to open a file, it raises a "IOError" exception. """ import os import gzip self.log.debug("OpenFile()") self.log.debug("Filename :" + FileName) if FileName is None: FileName = "PROCAR" self.log.debug("Input was None, now is: " + FileName) # checking if fileName is just a path and needs a "PROCAR to " be # appended elif os.path.isdir(FileName): self.log.info("The filename is a directory") if FileName[-1] != r"/": FileName += "/" FileName += "PROCAR" self.log.debug("I will try to open :" + FileName) # checking that the file exist if os.path.isfile(FileName): self.log.debug("The File does exist") # Checking if compressed if FileName[-2:] == "gz": self.log.info("A gzipped file found") inFile = gzip.open(FileName, mode="rt") else: self.log.debug("A normal file found") inFile = open(FileName, "r") return inFile # otherwise a gzipped version may exist elif os.path.isfile(FileName + ".gz"): self.log.info( "File not found, however a .gz version does exist and will" " be used" ) inFile = gzip.open(FileName + ".gz", mode="rt") else: self.log.debug("File not exist, neither a gzipped version") print(FileName) raise IOError("File not found") self.log.debug("OpenFile()...done") return inFile
[docs] def MergeFiles(self, inFiles, outFile, gzipOut=False): """ Concatenate two or more PROCAR files. This methods takes care of the k-indexes. Useful when large number of K points have been calculated in different PROCARs. Args: -inFiles: an iterable with files to be concatenated -outFile: a string with the outfile name. -gzipOut: whether gzip or not the outout file. Warning: spin polarized case is not Ok! """ import gzip self.log.debug("MergeFiles()") self.log.debug("infiles: " " ,".join(inFiles)) inFiles = [self.OpenFile(x) for x in inFiles] header = [x.readline() for x in inFiles] self.log.debug("All the input headers are: \n" + "".join(header)) metas = [x.readline() for x in inFiles] self.log.debug("All the input metalines are:\n " + "".join(metas)) # parsing metalines parsedMeta = [list(map(int, re.findall(r"#[^:]+:([^#]+)", x))) for x in metas] kpoints = [x[0] for x in parsedMeta] bands = set([x[1] for x in parsedMeta]) ions = set([x[2] for x in parsedMeta]) # checking that bands and ions match (mind: bands & ions are 'sets'): if len(bands) != 1 or len(ions) != 1: self.log.error("Number of bands/ions do not match") raise RuntimeError("Files are incompatible") newKpoints = np.array(kpoints, dtype=int).sum() self.log.info("New number of Kpoints: " + str(newKpoints)) newMeta = metas[0].replace(str(kpoints[0]), str(newKpoints), 1) self.log.debug("New meta line:\n" + newMeta) if gzipOut: self.log.debug("gzipped output") outFile = gzip.open(outFile, mode="wt") else: self.log.debug("normal output") outFile = open(outFile, "w") outFile.write(header[0]) outFile.write(newMeta) # embedded function to change old k-point indexes by the correct # ones. The `kreplace.k` syntax is for making the variable 'static' def kreplace(matchobj): # self.log.debug(print matchobj.group(0)) kreplace.k += 1 kreplace.localCounter += 1 return matchobj.group(0).replace( str(kreplace.localCounter), str(kreplace.k) ) kreplace.k = 0 down = [] # to handle spin-down (if found) self.log.debug("Going to replace K-points indexes") for inFile in inFiles: lines = inFile.read() # looking for an extra metada line, if found the file is # spin-polarized p = re.compile( r"#[\s\w]+k-points:[\s\d]+#[\s\w]+bands:[\s\d]+#[\w\s]+ions:\s*\d+\s*" ) lines = p.split(lines) up = lines[0] if len(lines) == 2: down.append(lines[1]) self.log.info("Spin-polarized PROCAR!") # closing inFile inFile.close() kreplace.localCounter = 0 up = re.sub("(\s+k-point\s*\d+\s*:)", kreplace, up) outFile.write(up) # handling the spin-down channel, if present if down: self.log.debug("writing spin down metadata") outFile.write("\n") outFile.write(newMeta) kreplace.k = 0 for group in down: kreplace.localCounter = 0 group = re.sub("(\s+k-point\s*\d+\s*:)", kreplace, group) outFile.write(group) self.log.debug("Closing output file") outFile.close() self.log.debug("MergeFiles()...done") return
[docs] def FermiOutcar(self, filename): """Just finds all E-fermi fields in the outcar file and keeps the last one (if more than one found). Args: -filename: the file name of the outcar to be readed """ self.log.debug("FermiOutcar(): ...") self.log.debug("Input filename : " + filename) outcar = open(filename, "r").read() match = re.findall(r"E-fermi\s*:\s*(-?\d+.\d+)", outcar)[-1] self.log.info("Fermi Energy found : " + match) self.log.debug("FermiOutcar(): ...Done") return float(match)
[docs] def RecLatOutcar(self, filename): """Finds and return the reciprocal lattice vectors, if more than one set present, it return just the last one. Args: -filename: the name of the outcar file to be read """ self.log.debug("RecLatOutcar(): ...") self.log.debug("Input filename : " + filename) outcar = open(filename, "r").read() # just keeping the last component recLat = re.findall(r"reciprocal\s*lattice\s*vectors\s*([-.\s\d]*)", outcar)[-1] self.log.debug("the match is : " + recLat) recLat = recLat.split() recLat = np.array(recLat, dtype=float) # up to now I have, both direct and rec. lattices (3+3=6 columns) recLat.shape = (3, 6) recLat = recLat[:, 3:] self.log.info("Reciprocal Lattice found :\n" + str(recLat)) self.log.debug("RecLatOutcar(): ...Done") return recLat
[docs] def ProcarRepair(self, infilename, outfilename): """It Tries to repair some stupid problems due the stupid fixed format of the stupid fortran. Up to now it only separes k-points as the following: k-point 61 : 0.00000000-0.50000000 0.00000000 ... to k-point 61 : 0.00000000 -0.50000000 0.00000000 ... But as I found new stupid errors they should be fixed here. """ self.log.debug("ProcarRepair(): ...") infile = self.OpenFile(infilename) fileStr = infile.read() infile.close() # Fixing bands issues (when there are more than 999 bands) # band *** # energy 6.49554019 # occ. 0.00000000 fileStr = re.sub(r"(band\s)(\*\*\*)", r"\1 1000", fileStr) # Fixing k-point issues fileStr = re.sub(r"(\.\d{8})(\d{2}\.)", r"\1 \2", fileStr) fileStr = re.sub(r"(\d)-(\d)", r"\1 -\2", fileStr) fileStr = re.sub(r"\*+", r" -10.0000 ", fileStr) outfile = open(outfilename, "w") outfile.write(fileStr) outfile.close() self.log.debug("ProcarRepair(): ...Done") return