Electrostatic Potential (Quantum Espresso) - Courtesy of Andrew Doyle
####################################################### ## Tutorial by: Andrew ## Topic: Electrostatic potential in ase-espresso ## ## dependencies: ase, espresso, pickle ## ## Generates the data files necessary to analyze ## the charge density and electrostatic potential ## in and around a Pt(100) slab ####################################################### # Import necessary packages from ase import Atoms from ase.io import write from ase.lattice.surface import fcc100 from espresso import espresso import pickle # Generate the representation of the atoms in ASE slab = fcc100('Pt', size=(2,2,3), vacuum=10.0) # Create an espresso calculator object # (Tailor for desired settings and accuracy) calc = espresso(pw=400 , # Plane wave cutoff dw=4000, # Density wave cutoff kpts=(3,3,1), # (rather sparse) k-point (Brillouin) sampling xc='RPBE', #Exchange-correlation functional dipole={'status':True}, # Includes dipole correction (necessary for asymmetric slabs) outdir='pt100_analysis') # Espresso-generated files will be put here # Attach the calculator object to the atoms slab.set_calculator(calc) # Perform a static calculation to generate wavefunctions slab.get_potential_energy() # Create an image of the atoms write('slab.png',slab) # Get the electrostatic potential potential = calc.extract_ionic_and_hartree_potential() # Create and populate output file for electrostatic potential potential_file=open('potential.pickle','w') pickle.dump(potential,potential_file) potential_file.close()
Now we are done with the intensive calculations. The following scripts will interpret the results and can be run easily on any computer in a matter of seconds.
####################################################### ## Tutorial by: Andrew ## Topic: Plotting electrostatic potential data from ## ase-espresso ## ## dependencies: pickle, pylab, numpy ## ## Plots an XY-averaged electrostatic potential as ## a function of Z position through and outside ## a Pt(100) slab. ####################################################### # Import necessary packages import pickle import pylab as plt import numpy as np # Retrieves the data generated in the previous script potential_file = open('potential.pickle') potential = pickle.load(potential_file) origin=potential[0] # [X0,Y0,Z0] - The positions of the origin of the cell. Here they are [0,0,0] cell = potential[1] # [3x3 Matrix] Contains the [x,y,z] components of the 3 cell vectors cell_x = cell[0][0] # The x component of the first cell vector (Remember: This cell is rectangular) cell_y = cell[1][1] # The y component of the second cell vector (Remember: This cell is rectangular) cell_z = cell[2][2] # The z component of the third cell vector (Remember: This cell is rectangular) v = potential[2] # Contains all real-space potential field points. ####################################################### ## Understanding the architecture of the rest of the ## data can be tricky. Using the variables presented ## here, V(x,y,z)=v[x][y][z]. ## ## First we aim to extract all the information ## available to us, based on numpy's architecture. ####################################################### n_x = len(v) # Returns the number of entries in the first subset (number of X entries) n_y = len(v[0]) # Returns the number of entries in the second subset (number of Y entries - choice of '0' was arbitrary) n_z = len(v[0][0]) # Returns the number of entries in the third subset (number of Z entries - choice of '0' was arbitrary) potential = [] # Initializes a list for average potentials # Iterate over all space to average and process potential information for z in range(n_z): temp = 0 # Will serve as a partial sum for averaging purposes for x in range(n_x): for y in range(n_y): temp+=v[x][y][z] # Sums the potential at all points in an X-Y plane temp = temp / (n_x*n_y) # Divides by the total number of points potential.append( temp ) # Stores the value for average potential at this Z position # Translates the indices of points along the Z axis into real-space positions z_coordinate = np.linspace( 0 , cell_z , n_z ) # Plots the data, formats plot plt.plot(z_coordinate, potential) plt.xlabel(r'z ($\AA$)',size=18) plt.ylabel(r'$\langle V \ \rangle_{XY}$ (Volts)',size=18) plt.title('Electrostatics of Pt(100)',size=18) # Display plot plt.show() # If you'd prefer to save the plot, highlight the line above and uncomment the line below #plt.savefig('potential_analysis.png')
The end result should look something like this:
Plot self-consistent field convergence data
This script, when run in a quantum espresso job folder, will plot the estimated SCF accuracy and total DFT energy as a function of electronic step. This can be useful for diagnosing weird convergence behavior, or for impatiently waiting for the next ionic step to finish.
#!/home/vossj/suncat/bin/python import os import pylab as plt import sys import matplotlib.pyplot as plt2 from mpl_toolkits.axes_grid1 import host_subplot import mpl_toolkits.axisartist as AA rydberg = 13.6057 #rydberg to eV conversion # default to outdir/log if no log is given if len(sys.argv) > 1: log = sys.argv[1] else: log = 'outdir/log' filename = 'scf_temp.txt' filename2 = 'etmp.txt' os.system('grep scf %s > %s'%(log,filename)) os.system("grep 'total energy' %s > %s"%(log,filename2)) file = open(filename) lines = file.read().split('\n') lines = filter(None,lines) file2 = open(filename2) lines2 = file2.read().split('\n') lines2 = filter(None,lines2) scf = [] energy = [] iter = [] iter2 = [] n = 0 # append scf convergence data from log file to scf for line in lines: items = filter(None, line.split(' ')) try: scf.append( float(items[4])*rydberg ) n += 1 iter.append(n) except: continue # append total dft energy data from log to energy n = 0 for line in lines2: items = filter(None, line.split(' ')) try: n += 1 energy.append(float(items[3])*rydberg ) iter2.append(n) except: continue # find the plane-wave cutoff from the log file os.system("grep 'kinetic-energy cutoff' %s > %s"%(log,filename)) file = open(filename) items = filter(None, file.read().split(' ')) pw = float(items[3]) * 13.606 os.system('rm %s'%filename) os.system('rm %s'%filename2) # plot everything nicely host = host_subplot(111,axes_class = AA.Axes) plt2.subplots_adjust(right=0.75) par1 = host.twinx() host.set_xlabel('Number of Iterations') host.set_ylabel('Estimated SCF Accuracy (eV)',color='red') par1.set_ylabel('DFT Total-Energy (eV)',color='green') p1, = host.plot(iter,scf,label='SCF Acc') p2, = par1.plot(iter2,energy,label='DFT Energy') host.set_yscale('log') plt.semilogy(iter,scf) plt.xlabel('Number of Iterations') plt.ylabel('Estimated SCF Accuracy (eV)') plt.title(r'$PW_{Cut}$=%d eV'%pw) plt.tight_layout() plt.show()
The output should look something like this, for a nicely converging run with several ionic steps. Multiple ionic step directories will contain all ionic steps in the plot.