Skip to content

Instantly share code, notes, and snippets.

@Ionizing
Last active August 1, 2024 01:19
Show Gist options
  • Save Ionizing/1ac92f98e8b00a1cf6f16bd57694ff03 to your computer and use it in GitHub Desktop.
Save Ionizing/1ac92f98e8b00a1cf6f16bd57694ff03 to your computer and use it in GitHub Desktop.
A script to plot work function using LOCPOT.
#!/usr/bin/env python3
'''
A script to plot work function using LOCPOT.
Requirement: python3, numpy, matplotlib, ase
Author: @Ionizing
Date: 22:46, Jan 11th, 2021.
CHANGELOG:
- 1:56, Jan 25th, 2021
- Add E-fermi level correction
- More logging info
- 10:05, Dec 28th, 2021
- Fix E-fermi extraction error, return the last one if there are many 'E-fermi' items.
'''
from argparse import ArgumentParser
import os
import logging
import re
import numpy as np
import matplotlib.pyplot as plt
from ase.calculators.vasp import VaspChargeDensity
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def locpot_mean(fname="LOCPOT", axis='z', savefile='locpot.dat', outcar="OUTCAR"):
'''
Reads the LOCPOT file and calculate the average potential along `axis`.
@in: See function argument.
@out:
- xvals: grid data along selected axis;
- mean: averaged potential corresponding to `xvals`.
'''
def get_efermi(outcar="OUTCAR"):
if not os.path.isfile(outcar):
logger.warning("OUTCAR file not found. E-fermi set to 0.0eV")
return None
txt = open(outcar).read()
efermi = re.findall(r'E-fermi :\s*([-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?)', txt)[-1][0]
logger.info("Found E-fermi = {}".format(efermi))
return float(efermi)
logger.info("Loading LOCPOT file {}".format(fname))
locd = VaspChargeDensity(fname)
cell = locd.atoms[0].cell
latlens = np.linalg.norm(cell, axis=1)
vol = np.linalg.det(cell)
iaxis = ['x', 'y', 'z'].index(axis.lower())
axes = [0, 1, 2]
axes.remove(iaxis)
axes = tuple(axes)
locpot = locd.chg[0]
# must multiply with cell volume, similar to CHGCAR
logger.info("Calculating workfunction along {} axis".format(axis))
mean = np.mean(locpot, axes) * vol
xvals = np.linspace(0, latlens[iaxis], locpot.shape[iaxis])
# save to 'locpot.dat'
efermi = get_efermi(outcar)
logger.info("Saving raw data to {}".format(savefile))
if efermi is None:
np.savetxt(savefile, np.c_[xvals, mean],
fmt='%13.5f', header='Distance(A) Potential(eV) # E-fermi not corrected')
else:
mean -= efermi
np.savetxt(savefile, np.c_[xvals, mean],
fmt='%13.5f', header='Distance(A) Potential(eV) # E-fermi shifted to 0.0eV')
return (xvals, mean)
def parse_cml_arguments():
parser = ArgumentParser(
description='A tool to plot work function according to LOCPOT', add_help=True)
parser.add_argument('-a', '--axis', type=str, action='store',
help='Which axis to be calculated: x, y or z. Default by z', default='z', choices=['x', 'y', 'z'])
parser.add_argument('input', nargs='?', type=str,
help='The input file name, default by LOCPOT', default='LOCPOT')
parser.add_argument('-w', '--write', type=str, action='store',
help='Save raw work function data to file, default by locpot.dat', default='locpot.dat')
parser.add_argument('-o', '--output', type=str, action='store',
help='Output image file name, default by Workfunction.png', default='Workfunction.png')
parser.add_argument('--dpi', type=int, action='store',
help='DPI of output image, default by 400', default=400)
parser.add_argument('--title', type=str, action='store',
help='Title in output image. If none, no title is added, default is None', default=None)
return parser.parse_args()
if '__main__' == __name__:
args = parse_cml_arguments()
x, y = locpot_mean(args.input, args.axis, args.write)
logger.info("Plotting to image")
plt.plot(x, y, color='k')
plt.xlabel('Distance(A)')
plt.ylabel('Potential(eV)')
plt.grid(color='gray', ls='-.')
plt.xlim(0, np.max(x))
plt.ylim(np.max(y)-2, np.max(y)+0.5)
plt.minorticks_on()
if args.title:
plt.title(args.title)
logger.info("Saving to {}".format(args.output))
plt.savefig(args.output, dpi=args.dpi)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment