Source code for pyprocar.scripts.scriptCat

__author__ = "Pedram Tavadze and Logan Lang"
__maintainer__ = "Pedram Tavadze and Logan Lang"
__email__ = "petavazohi@mail.wvu.edu, lllang@mix.wvu.edu"
__date__ = "March 31, 2020"


import os
from typing import List
import re
import glob

import numpy as np

from ..utils import welcome
from ..utils import UtilsProcar
from ..io.abinit import Output

[docs] def cat( inFiles:List[str]=None, outFile:str="PROCAR_merged", gz:bool=False, mergeparallel:bool=False, fixformat:bool=False, nspin:int=1, abinit_output:str=None, ): """ This module concatenates multiple PROCARs. If a list of input PROCAR files is not provided it will merge all the PROCAR_* files in the directory. Parameters ---------- inFiles : List[str], optional A list of PROCAR files to concatenate, by default None outFile : str, optional String for the output, by default "PROCAR_merged" gz : bool, optional Boolean if output should be compressed to .gz file, by default False mergeparallel : bool, optional Boolean for merging PROCARs generated from parallel Abinit calculations., by default False fixformat : bool, optional Boolean to fix formatting issues in the Abinit PROCAR file., by default False nspin : int, optional To detect if the calculation is spin-colinear it checks for the nsppol flag in the Abinit output file as set in abinit_output. If not present, set nspin., by default 1 abinit_output : str, optional The abinit output file, by default None """ welcome() # reading in all PROCAR_* files and putting it into a list if not provided. if inFiles is None: inFiles = sorted(glob.glob("PROCAR_*")) else: pass print("Concatenating...") print("Input : ", inFiles) # ', '.join(inFiles) print("Output : ", outFile) if mergeparallel == False and fixformat == False: if gz == True: print("out compressed: true") if gz == True and outFile[-3:] != ".gz": outFile += ".gz" print(".gz extension appended to the outfile") handler = UtilsProcar() handler.MergeFiles(inFiles, outFile, gzipOut=gz) return elif mergeparallel == True and fixformat == False: _mergeparallel(inFiles, outFile, nspin, abinit_output) elif mergeparallel == True and fixformat == True: outFile_temp = "outFile.tmp" _mergeparallel(inFiles, outFile_temp, nspin, abinit_output) _fixformat(outFile_temp, outFile) if os.path.exists(outFile_temp): os.remove(outFile_temp) elif mergeparallel == False and fixformat == True: print("Using fixformat = True without mergeparallel. Input a single PROCAR.") _fixformat(inFiles, outFile)
def _mergeparallel(inputfiles=None, outputfile=None, nspin=1, abinit_output=None): """ This merges Procar files seperated between k-point ranges. Happens with parallel Abinit runs. """ print("Merging parallel files...") filenames = sorted(inputfiles) # creating an instance of the AbinitParser class if abinit_output: abinitparserobject = Output(abinit_output=abinit_output) nspin = int(abinitparserobject.nspin) else: nspin = int(nspin) if nspin != 2: with open(outputfile, "w") as outfile: for fname in filenames: with open(fname) as infile: for line in infile: outfile.write(line) elif nspin == 2: # for spin polarized calculations the spin down segments are saved in the # second half of the PROCAR's but in reverse k-point order. So we have to # fix the order and merge the second half of the PROCAR's. spinup_list = filenames[: int(len(filenames) / 2)] spindown_list = filenames[int(len(filenames) / 2) :] # reading the second line of the header to set as the separating line # in the colinear spin PROCAR. fp = open(spinup_list[0], "r") header1 = fp.readline() header2 = fp.readline() fp.close() # second half of PROCAR files in reverse order. spindown_list.reverse() # Writing new PROCAR with first spin up, header2 and then # spin down (reversed). with open(outputfile, "w") as outfile: for spinupfile in spinup_list: with open(spinupfile) as infile: for line in infile: outfile.write(line) outfile.write("\n") outfile.write(header2) outfile.write("\n") for spindownfile in spindown_list: with open(spindownfile) as infile: for line in infile: outfile.write(line) def _fixformat(inputfile=None, outputfile=None): """Fixes the formatting of Abinit's Procar when the tot projection is not summed and spin directions not seperated. """ print("Fixing formatting errors...") # removing existing temporary fixed file if os.path.exists(outputfile): os.remove(outputfile) ####### Fixing the parallel PROCARs from Abinit ########## rf = open(inputfile, "r") data = rf.read() rf.close() # reading headers rffl = open(inputfile, "r") first_line = rffl.readline() rffl.close() # header header = re.findall("#\sof\s.*", data)[0] # writing to PROCAR fp = open(outputfile, "a") fp.write(first_line) fp.write(str(header) + "\n\n") # get all the k-point line headers kpoints_raw = re.findall("k-point\s*[0-9]\s*:*.*", data) for kpoint_counter in range(len(kpoints_raw)): if kpoint_counter == (len(kpoints_raw) - 1): # get bands of last k point bands_raw = re.findall( kpoints_raw[kpoint_counter] + "([a-z0-9\s\n.+#-]*)", data )[0] else: # get bands between k point n and n+1 bands_raw = re.findall( kpoints_raw[kpoint_counter] + "([a-z0-9\s\n.+#-]*)" + kpoints_raw[kpoint_counter + 1], data, )[0] # get the bands headers for a certain k point raw_bands = re.findall("band\s*[0-9]*.*", bands_raw) # writing k point header to file fp.write(kpoints_raw[kpoint_counter] + "\n\n") for band_counter in range(len(raw_bands)): if band_counter == (len(raw_bands) - 1): # the last band single_band = re.findall( raw_bands[band_counter] + "([a-z0-9.+\s\n-]*)", bands_raw )[0] else: # get a single band single_band = re.findall( raw_bands[band_counter] + "([a-z0-9.+\s\n-]*)" + raw_bands[band_counter + 1], bands_raw, )[0] # get the column headers for ion, orbitals and total column_header = re.findall("ion\s.*tot", single_band)[0] # get number of ions using PROCAR file nion_raw = re.findall("#\s*of\s*ions:\s*[0-9]*", data)[0] nion = int(nion_raw.split(" ")[-1]) # the first column of the band. Same as ions first_column = [] for x in single_band.split("\n"): if x != "": if x != " ": if x.split()[0] != "ion": first_column.append(x.split()[0]) # number of spin orientations norient = int(len(first_column) / nion) # calculate the number of orbital headers (s,p,d etc.) for x in single_band.split("\n"): if x != "": if x != " ": if x.split()[0] == "ion": norbital = len(x.split()) - 2 # array to store a single band data as seperate lines single_band_lines = [] for x in single_band.split("\n"): if x != "": if x != " ": if x.split()[0] != "ion": single_band_lines.append(x) # create empty array to store data (the orbitals + tot) bands_orb = np.zeros(shape=(norient, nion, norbital + 1)) # enter data into bands_orb iion = 0 iorient = 0 for x in single_band.split("\n"): if x != "" and x != " " and x.split()[0] != "ion": iline = x.split() if iion > 1: iion = 0 iorient += 1 for iorb in range(0, norbital + 1): bands_orb[iorient, iion, iorb] = float(iline[iorb + 1]) iion += 1 # create an array to store the total values tot = np.zeros(shape=(norient, norbital + 1)) # entering data into tot array for iorient in range(norient): tot[iorient, :] = np.sum(bands_orb[iorient, :, :], axis=0) # writing data fp.write(raw_bands[band_counter] + "\n\n") fp.write(column_header + "\n") band_iterator = 0 total_count = 0 for orientations_count in range(norient): for ions_count in range(nion): fp.write(single_band_lines[band_iterator] + "\n") band_iterator += 1 fp.write("tot " + " ".join(map(str, tot[total_count, :])) + "\n\n") total_count += 1 fp.close()