├── .gitignore ├── README.md ├── modes_to_vesta.py └── pics └── ZrIN.png /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | .*~ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generate VESTA mode files from VASP OUTCAR files 2 | 3 | ### What this code does: 4 | Automatically generates visualizations of vibrational modes 5 | computed from the density functional theory (DFT) code 6 | [VASP](https://www.vasp.at/), like this acoustic mode of ZrIN: 7 | 8 | 9 | 10 | 11 | 12 | ### Details: 13 | The file `modes_to_vesta.py` converts the modes calculated in 14 | a VASP density functional perturbation theory (DFPT) calculation (IBRION = 7 or 8) to 15 | the Visualization for Electronic and STructural Analysis [VESTA](http://jp-minerals.org/vesta/en/) file format. The final result is a VESTA file that allows you to visualize each vibrational mode computed from DFPT. 16 | 17 | ### Usage: 18 | 19 | In a single folder, include the following 4 files: 20 | 21 | 1. `POSCAR`, which gives the atomic positions and unit cell used as input to the DFPT calculation 22 | 2. `OUTCAR`, which should hold the results of an IBRION=7 or 8 calculation 23 | 3. `poscar.vesta`. This file is the direct result of opening the `POSCAR` file directly in VESTA and saving the file with the filename `poscar` and extension `.vesta` (note that poscar is lower-case). No modes/displacement vectors should be visible in VESTA prior to saving. 24 | 4. `modes_to_vesta.py`, which comes from this repo. 25 | 26 | To run, do the following: 27 | 28 | $ python modes_to_vesta.py 29 | 30 | 31 | Only the first two arguments are needed; the third argument is optional and can be used to specify an alternative VESTA filename (i.e., a name different from `poscar.vesta`) 32 | 33 | The result is a set of 3N files where N is the number of atoms in the unit cell. Each file has the format `mode_.vesta`, where `` is the mode frequency in cm-1. 34 | 35 | 36 | *Some parts of `modes_to_vesta.py` have been adapted from [vasp_raman.py](https://github.com/raman-sc/VASP) and [Phonopy](https://atztogo.github.io/phonopy/)* 37 | -------------------------------------------------------------------------------- /modes_to_vesta.py: -------------------------------------------------------------------------------- 1 | from pylab import * 2 | import sys 3 | import re 4 | 5 | 6 | def MAT_m_VEC(m, v): 7 | p = [ 0.0 for i in range(len(v)) ] 8 | for i in range(len(m)): 9 | assert len(v) == len(m[i]), 'Length of the matrix row is not equal to the length of the vector' 10 | p[i] = sum( [ m[i][j]*v[j] for j in range(len(v)) ] ) 11 | return p 12 | 13 | 14 | def T(m): 15 | p = [[ m[i][j] for i in range(len( m[j] )) ] for j in range(len( m )) ] 16 | return p 17 | 18 | 19 | def parse_poscar(poscar): 20 | # modified subroutine from phonopy 1.8.3 (New BSD license) 21 | poscar.seek(0) # just in case 22 | lines = poscar.readlines() 23 | 24 | scale = float(lines[1]) 25 | if scale < 0.0: 26 | print("[parse_poscar]: ERROR negative scale not implemented.") 27 | sys.exit(1) 28 | 29 | b = [] 30 | for i in range(2, 5): 31 | b.append([float(x)*scale for x in lines[i].split()[:3]]) 32 | 33 | vol = b[0][0]*b[1][1]*b[2][2] + b[1][0]*b[2][1]*b[0][2] + b[2][0]*b[0][1]*b[1][2] - \ 34 | b[0][2]*b[1][1]*b[2][0] - b[2][1]*b[1][2]*b[0][0] - b[2][2]*b[0][1]*b[1][0] 35 | 36 | try: 37 | num_atoms = [int(x) for x in lines[5].split()] 38 | line_at = 6 39 | except ValueError: 40 | symbols = [x for x in lines[5].split()] 41 | num_atoms = [int(x) for x in lines[6].split()] 42 | line_at = 7 43 | nat = sum(num_atoms) 44 | 45 | if lines[line_at][0].lower() == 's': 46 | line_at += 1 47 | 48 | if (lines[line_at][0].lower() == 'c' or lines[line_at][0].lower() == 'k'): 49 | is_scaled = False 50 | else: 51 | is_scaled = True 52 | 53 | line_at += 1 54 | positions = [] 55 | for i in range(line_at, line_at + nat): 56 | pos = [float(x) for x in lines[i].split()[:3]] 57 | if is_scaled: 58 | pos = MAT_m_VEC(T(b), pos) 59 | positions.append(pos) 60 | poscar_header = ''.join(lines[1:line_at-1]) # will add title and 'Cartesian' later 61 | 62 | return nat, vol, b, positions, poscar_header 63 | 64 | 65 | 66 | def parseModes(outcar, nat, vesta_front, vesta_end, scaling_factor): 67 | 68 | eigvals = [ 0.0 for i in range(nat*3) ] 69 | eigvecs = [ 0.0 for i in range(nat*3) ] 70 | norms = [ 0.0 for i in range(nat*3) ] 71 | 72 | outcar.seek(0) # just in case 73 | while True: 74 | line = outcar.readline() 75 | if not line: 76 | break 77 | if "Eigenvectors and eigenvalues of the dynamical matrix" in line: 78 | 79 | outcar.readline() # empty line 80 | outcar.readline() # Eigenvectors and eigenvalues of the dynamical matrix 81 | #outcar.readline() # ---------------------------------------------------- 82 | #outcar.readline() # empty line 83 | print("Mode Freq (cm-1)") 84 | for i in range(nat*3): 85 | outcar.readline() # empty line 86 | p = re.search(r'^\s*(\d+).+?([\.\d]+) cm-1', outcar.readline()) 87 | eigvals[i] = float(p.group(2)) 88 | 89 | outcar.readline() # X Y Z dx dy dz 90 | eigvec = [] 91 | 92 | for j in range(nat): 93 | tmp = outcar.readline().split() 94 | eigvec.append([ float(tmp[x]) for x in range(3,6) ]) 95 | eigvecs[i] = eigvec 96 | norms[i] = sqrt( sum( [abs(x)**2 for sublist in eigvec for x in sublist] ) ) 97 | writeVestaMode(i, eigvals[i], eigvecs[i], vesta_front, vesta_end, nat, scaling_factor) 98 | print("%4d %6.2f" %(i+1, eigvals[i])) 99 | 100 | if "Eigenvectors after division by SQRT(mass)" in line: 101 | break 102 | 103 | return eigvals, eigvecs, norms 104 | 105 | 106 | def writeVestaMode(i, eigval, eigvec, vesta_front, vesta_end, nat, scaling_factor): 107 | modef = open("mode_%.2f.vesta"%eigval, 'w') 108 | 109 | modef.write(vesta_front) 110 | 111 | sf = scaling_factor 112 | towrite = "VECTR\n" 113 | for i in range(1,1+nat): 114 | towrite += "%4d%9.5f%9.5f%9.5f\n"%(i,eigvec[i-1][0]*sf,eigvec[i-1][1]*sf,eigvec[i-1][2]*sf) 115 | towrite += "%5d 0 0 0 0\n 0 0 0 0 0\n"%i 116 | towrite += " 0 0 0 0 0\n" 117 | towrite += "VECTT\n" 118 | for i in range(1,1+nat): 119 | towrite += "%4d%6.3f 255 0 0 1\n"%(i,0.5) 120 | towrite += " 0 0 0 0 0\n" 121 | 122 | if i==0: 123 | print(towrite) 124 | 125 | modef.write(towrite) 126 | 127 | return 0 128 | 129 | 130 | def openVestaOutcarPoscar(): 131 | if len(sys.argv) == 1: 132 | try: 133 | vesta = open('poscar.vesta','r') 134 | except: 135 | print("Cannot find poscar.vesta in current directory") 136 | print("Usage:\n\tpython modes_to_vesta.py ") 137 | sys.exit(0) 138 | elif len(sys.argv) == 2: 139 | try: 140 | print("Opening ", sys.argv[1]) 141 | vesta = open(sys.argv[1],'w') 142 | except: 143 | print("Cannot find file ", sys.argv[1]) 144 | sys.exit(0) 145 | else: 146 | print("Cannot parse >1 command-line argument") 147 | sys.exit(0) 148 | 149 | try: 150 | outcar = open('OUTCAR', 'r') 151 | except: 152 | print("Cannot find OUTCAR in current directory") 153 | sys.exit(0) 154 | 155 | try: 156 | poscar = open('POSCAR', 'r') 157 | except: 158 | print("Cannot find POSCAR in current directory") 159 | sys.exit(0) 160 | 161 | return vesta, outcar, poscar 162 | 163 | 164 | def getVestaFrontEnd(vesta): 165 | 166 | vfile = vesta.read() 167 | vesta_front = vfile.split("VECTR")[0] 168 | vesta_end = vfile.split("VECTT\n 0 0 0 0 0")[1] 169 | 170 | return vesta_front, vesta_end 171 | 172 | if __name__ == '__main__': 173 | 174 | scaling_factor = 6 # 6 is roughly correct 175 | 176 | vesta, outcar, poscar = openVestaOutcarPoscar() 177 | vesta_front, vesta_end = getVestaFrontEnd(vesta) 178 | nat, vol, b, positions, poscar_header = parse_poscar(poscar) 179 | 180 | print("# atoms vol of unit cell (Ang^3) # modes") 181 | print(" %d %4.2f %d" %(nat,vol,nat*3)) 182 | 183 | parseModes(outcar, nat, vesta_front, vesta_end, scaling_factor) 184 | -------------------------------------------------------------------------------- /pics/ZrIN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Stanford-MCTG/VASP-plot-modes/6da63708734a70becf32b3493edaac5831fe7dc9/pics/ZrIN.png --------------------------------------------------------------------------------