├── .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
--------------------------------------------------------------------------------