#!/usr/bin/env python3

######  SUM STATES #######
# Python script for summing and ploting the data from the Density Of States
# files obtained from projwfc.x. It can sum also k-solved dos, and make a plot
# with mathplotlib (if not available, gnuplot, if not avaible, print to file)
# if there is not X11 forwarding, plots in terminal.
# It does something very similar to sumpdos.f90, but with
# some extra features (use "-h" option).
#
# it takes two different inputs, the first one is the pw.x output
# ("-o" option), which is used for parsing the Fermi energy for fitting
# the PDOS curve to the right energy. The other files are the pDOS files
# ("-s" option), that can be given with shell syntax, i.e.
# pdos_atm*Fe*wfc*d* for summing all the d orbitals of Fe.
# It can also handle k solved dos files.
#
# One of the most useful feature, compared to the sumpdos.x, is the
# fact that it also builds the picture directly, so it can be directly
# visualized and exported for inclusion in a document.
# It uses mathplotlib for plotting, but if no mathplotlib is found in
# the $PYTHONPATH, it tries to use gnuplot, if no gnuplot available,
# dumps the output data to a file.
# In the that no X11 forwarding is available (i.e. ssh to the cluster),
# it shows a rough graph in the terminal, so we get an idea of the shape
# of the results.
#
# Example of usage:
# cd ....../espresso-5.0/PP/examples/example02/results/
# ../../../src/sum_states.py -o ni.dos.out -s
# ni.pdos_atm#1\(Ni\)_wfc#2\(d\) -t "Example PP/02" -xr -6 2
#
#
# The procedure for obtaining the DOS files is explained
# i.e. in (espresso-dir)/PP/examples/example02/
#
# Author: Dr. Julen Larrucea
#         University of Bremen,
#         Bremen Centre for Computational Materials Science, HMI Group
#         julenl [at] gmail.com  or larrucea [at] hmi.uni-bremen.de
#
#  This file is distributed under the terms of the GNU General Public
#  License. See the file `License'
#  in the root directory of the present distribution,
#  or http://www.gnu.org/copyleft/gpl.txt .
#######################

import sys
import os
import fnmatch
import linecache

# Some default variables
version=0.2
pwout=""
selat="*"
graphtitle=""
min_x,max_x=-10,3
min_y,max_y="",""
output_file_name="sum_dos.out"
prt="no"

print(" #### sum_states.py version {} #### ".format(version))


# Check if X11, mathplotlib and gnuplot are available
try:
 os.popen("gnuplot -V").read()
 prog_gnuplot="yes" # gnuplot is installed
except:
 prog_gnuplot="no"



# Parse command line options
if len(sys.argv)>1:
 for i in sys.argv:
  if i.startswith('-'):
   option=i.split('-')[1]
   if option=="o":
     pwout= sys.argv[sys.argv.index('-o')+1]
   if option=="s":
    selat= sys.argv[sys.argv.index('-s')+1]
   if option=="p":
    prt="yes"
    if len(sys.argv) > sys.argv.index('-p')+1: # if there is a name after "-p" take it as an output name
     if sys.argv[sys.argv.index('-p')+1] != "-": # otherwise default name sum_dos.out
        dos_out_name=sys.argv[sys.argv.index('-p')+1]
   if option=="t":
    graphtitle= sys.argv[sys.argv.index('-t')+1]
   if option=="xr":
    min_x,max_x= float(sys.argv[sys.argv.index('-xr')+1]),float(sys.argv[sys.argv.index('-xr')+2])
   if option=="yr":
    min_y,max_y= float(sys.argv[sys.argv.index('-yr')+1]),float(sys.argv[sys.argv.index('-yr')+2])
   if option=="v":
    print("sum_dos.py version:", version)
    sys.exit()
   if option=="h":
    print('''
    -o QE output file name (for grepping Fermi E)
    -s Selection of atoms for summing the DOSes. "*" for all, *1*Fe*d* for first Fe atom " (def. "*")
    -p Print output to a file and aditionaly provide an output name (def. no output and "sum_dos.out")
    -t set title in the head of the graph
    -xr set min and max x value for the axes in the graph
    -yr set min and max y value for the axes in the graph
    -h print this help
    -v print version
       Example: sum_states.py --s sys.pdos_atm#4\(Fe2\)_wfc#2\(d\) -t "Wustite LDA+U single Fe" -xr -9 4
   ''')
    sys.exit()


# Check for mathplotlib/gnuplot and import mpl if possible
if len(os.popen('echo $DISPLAY').read()) > 1:
  graphic_plot="yes"
  try:
   from pylab import *
   mplplot="yes"
   print("pylab imported")
  except:
   print("There is no mathplotlib installed. Using gnuplot.")
   mplplot="no"
   prt="yes"
else:
  print("No X11. Trying to plot on terminal")
  graphic_plot="no"
  if prog_gnuplot=="no":
   prt="yes"


# if not specified, try to find the espresso output, in order to parse the Fermi energy
if pwout == "":
 for filen in filter(os.path.isfile, os.listdir('.')):
  if "Program PWSCF" in linecache.getline(filen, 2):
   print("Using " + filen + " as pw.x output. You can specify another one with the -o option.")
   pwout=filen

# Parse Fermi energy from the pw.x output
if pwout!="":
 try:
  os.popen("grep -a 'the Fermi energy is' "+pwout ).read()
  fermi=float(os.popen("grep -a 'the Fermi energy is' "+pwout ).read().split()[4])
  print("Fermi energy = ", fermi, "a.u.")
 except:
  print("WARNING: No Fermi energy found. Using 0 e.V. instead")
  fermi=0
else:
 print("WARNING: No pw.x output found. Using E Fermi = 0 e.V.")
 fermi=0

# List of all DOS files to add
dosfiles=[]
for dfile in os.listdir('.'):
   if fnmatch.fnmatch(dfile, selat):
     dosfiles.append(dfile)
if len(dosfiles)==0:
 print("ERROR: Provide a (list of) valid DOS file(s)")
 sys.exit()

print("dosfiles list:", " ".join(dosfiles))

# Check wetter we have k-solved DOS
if open(dosfiles[0],'r').readline().split()[1]=="E":
  ksolved="no"
  print("no ksolved")
elif open(dosfiles[0],'r').readline().split()[1]=="ik":
  ksolved="yes"
  print("ksolved")

# Sum over all k-points and files
mat=[]  # matrix with total sum of ldos
for i in range(len(dosfiles)):
 mati=[] # temporal matrix for each DOS file "i"
 k=0
 for line in open(dosfiles[i],'r'):
  if len(line) > 10 and line.split()[0] != "#":

   if ksolved=="no":
      mati.append([float(line.split()[0]),float(line.split()[1]),float(line.split()[2])])

   if ksolved=="yes":
      ik = int(line.split()[0])
      if ik > k:  #if it is a different k block
         k=int(line.split()[0])
         oldmat=[] # temporal matrix for each k-point
      if ik == 1:
         mati.append([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])]) # append: energy, ldosup, ldosdw
      elif ik == k and k > 1:
         oldmat.append([float(line.split()[1]),float(line.split()[2]),float(line.split()[3])])
      elif len(line) < 5 and k > 1:  #if blank line, sum k-frame to the total
       for j in range(len(oldmat)):
         mati[j]=[mati[j][0],mati[j][1]+oldmat[j][1],mati[j][2]+oldmat[j][2]]

 if mat == []: # if it is the first dos file, copy total matrix (mat) = the first dos files's data
    mat=mati[:]
 else:
    for j in range(len(mati)): # if it is not the first file, sum values
        mat[j]=[mat[j][0],mat[j][1]+mati[j][1],mat[j][2]+mati[j][2]]


print("...ploting...")


if prt=="yes":
 out=open(output_file_name,"w")
x,y1,y2=[],[],[]
for i in mat:
 x.append(i[0]-fermi)
 y1.append(i[1])
 y2.append(-i[2])
 if prt=="yes":  # print to a file
  print(i[0] - fermi, i[1], i[2], file=out)
if prt=="yes":
 out.close()


if graphic_plot=="yes":
  # if there is matplotlib, generate a plot with it
  if mplplot=="yes":
    plot(x,y1,linewidth=1.0)
    plot(x,y2,linewidth=1.0)
    print(min(y2), max(y1))
    plt.title(graphtitle)
    plt.xlabel('E (eV)')
    plt.ylabel('States')
    plt.grid(True)
    plt.rcParams.update({'font.size': 22})
    plt.fill(x,y1,color='0.8')
    plt.fill(x,y2,color='0.9')
    if min_x and max_x:
      fromx, tox = min_x, max_x
    plt.axis([fromx, tox, min(y2), max(y1)])
    show()
  elif mplplot=="no" and prog_gnuplot=="yes":  # If no mathplotlib available, use gnuplot
    os.system("""echo "plot '{0}' using ($1-{1}):2 w l, '' u ($1-{1}):3 w l" | gnuplot -persist""".format(
        output_file_name, fermi))
elif graphic_plot=="no":  # If no X forwarding available, show graph in terminal
  if prog_gnuplot=="yes":
    os.system("""echo "set terminal dumb; plot '{0}' using ($1-{1}):2 w l, '' u ($1-{1}):3 w l" | gnuplot -persist""".format(
        output_file_name, fermi))
