tools4vasp 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Patrick Melix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.1
2
+ Name: tools4vasp
3
+ Version: 0.0.2
4
+ Summary: Python tools for VASP
5
+ Home-page: https://github.com/Tonner-Zech-Group/VASP-tools
6
+ Author: Patrick Melix
7
+ Author-email: Patrick Melix <patrick.melix@uni-leipzig.de>
8
+ Project-URL: Homepage, https://github.com/Tonner-Zech-Group/VASP-tools
9
+ Project-URL: Issues, https://github.com/Tonner-Zech-Group/VASP-tools/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Unix Shell
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: Unix
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: ase>=1.0
20
+ Requires-Dist: matplotlib>=3.8.4
21
+ Requires-Dist: natsort>=8.3.1
22
+ Requires-Dist: numpy>=1.24.3
23
+ Requires-Dist: pymatgen>=2023.5.10
24
+
25
+ # VASP-tools
26
+
27
+ Our collection of tools for pre- and post-processing VASP calculations. Mainly Python and Bash.
28
+
29
+ ## Installation
30
+
31
+ Clone this repository and run `pip install .` inside the main directory. If you want to always use the latest content of the repo you can use the 'developement' install of pip by running `pip install -e .`. Just doing `git pull` to get the latest content of the repo will then automatically result in the usage of the latest code without need to reinstall.
32
+
33
+ You can also use the latest release by installing it from PyPi:
34
+
35
+ ```bash
36
+ pip install orcatools
37
+ ```
38
+
39
+ ## Dependencies
40
+
41
+ Different for each script, but mainly
42
+
43
+ - [ASE](https://wiki.fysik.dtu.dk/ase/)
44
+ - [VTST](http://theory.cm.utexas.edu/vtsttools/)
45
+ - [Pymatgen](https://pymatgen.org/)
46
+ - [Geodesic Interpolation](https://github.com/virtualzx-nad/geodesic-interpolate)
47
+
48
+ ## Pre-Processing
49
+
50
+ - freq2mode: generates MODECAR and mass-weighted MODCAR files from frequency calculations
51
+
52
+ ## Post-Processing
53
+
54
+ - chgcar2cube.py: Convert CHGCAR-like files to cube files using Pymatgen and ASE.
55
+ - neb2movie.py: Convert VASP NEB to ASE ext-xyz movie, just like nebmovie.pl of VTST.
56
+ - poscar2nbands.py: Helper to get the NBANDS value for LOBSTER calculations using the current POSCAR, INCAR and POTCAR setup with 'standard' options.
57
+ - vasp2traj.py: Convert VASP geometry optimization output to ASE compatible ext-xyz trajectory file.
58
+ - vasp-check.py: Assert proper occupations and SCF+GO convergence in VASP using ASE.
59
+ - vaspGetEF.py: Creates a plot of energy and forces along multiple GO runs (e.g. for restart jobs). Gathers data in all numeric subfolders and this folder containing a vasprun.xml file (depth one) and combines them in a single plot.
60
+ - visualize-magnetization.sh: Creates a VMD visualisation state file for the magnetization denisty by splitting the CHGCAR (by running chgsplit.pl), converting it to a cube file (by running chgcar2cube.sh) and then creating representations for VMD.
61
+ - viewMode.py: Shows a graphical preview of a MODECAR file using ase gui
62
+ - plotIRC: Tool that creates a plot of VASP IRC calculations in both direction and is compatible with shifts in the starting structure.
63
+ - replace_potcar_symlinks.sh: Searches for POTCARS in subdirs and replaces them with symlinks. CAREFUL!
@@ -0,0 +1,39 @@
1
+ # VASP-tools
2
+
3
+ Our collection of tools for pre- and post-processing VASP calculations. Mainly Python and Bash.
4
+
5
+ ## Installation
6
+
7
+ Clone this repository and run `pip install .` inside the main directory. If you want to always use the latest content of the repo you can use the 'developement' install of pip by running `pip install -e .`. Just doing `git pull` to get the latest content of the repo will then automatically result in the usage of the latest code without need to reinstall.
8
+
9
+ You can also use the latest release by installing it from PyPi:
10
+
11
+ ```bash
12
+ pip install orcatools
13
+ ```
14
+
15
+ ## Dependencies
16
+
17
+ Different for each script, but mainly
18
+
19
+ - [ASE](https://wiki.fysik.dtu.dk/ase/)
20
+ - [VTST](http://theory.cm.utexas.edu/vtsttools/)
21
+ - [Pymatgen](https://pymatgen.org/)
22
+ - [Geodesic Interpolation](https://github.com/virtualzx-nad/geodesic-interpolate)
23
+
24
+ ## Pre-Processing
25
+
26
+ - freq2mode: generates MODECAR and mass-weighted MODCAR files from frequency calculations
27
+
28
+ ## Post-Processing
29
+
30
+ - chgcar2cube.py: Convert CHGCAR-like files to cube files using Pymatgen and ASE.
31
+ - neb2movie.py: Convert VASP NEB to ASE ext-xyz movie, just like nebmovie.pl of VTST.
32
+ - poscar2nbands.py: Helper to get the NBANDS value for LOBSTER calculations using the current POSCAR, INCAR and POTCAR setup with 'standard' options.
33
+ - vasp2traj.py: Convert VASP geometry optimization output to ASE compatible ext-xyz trajectory file.
34
+ - vasp-check.py: Assert proper occupations and SCF+GO convergence in VASP using ASE.
35
+ - vaspGetEF.py: Creates a plot of energy and forces along multiple GO runs (e.g. for restart jobs). Gathers data in all numeric subfolders and this folder containing a vasprun.xml file (depth one) and combines them in a single plot.
36
+ - visualize-magnetization.sh: Creates a VMD visualisation state file for the magnetization denisty by splitting the CHGCAR (by running chgsplit.pl), converting it to a cube file (by running chgcar2cube.sh) and then creating representations for VMD.
37
+ - viewMode.py: Shows a graphical preview of a MODECAR file using ase gui
38
+ - plotIRC: Tool that creates a plot of VASP IRC calculations in both direction and is compatible with shifts in the starting structure.
39
+ - replace_potcar_symlinks.sh: Searches for POTCARS in subdirs and replaces them with symlinks. CAREFUL!
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tools4vasp"
7
+ dependencies = [
8
+ 'ase >= 1.0',
9
+ 'matplotlib >= 3.8.4',
10
+ 'natsort >= 8.3.1',
11
+ 'numpy >= 1.24.3',
12
+ 'pymatgen >= 2023.5.10',
13
+ ]
14
+ version = "0.0.2"
15
+ authors = [
16
+ { name="Patrick Melix", email="patrick.melix@uni-leipzig.de" },
17
+ ]
18
+ description = "Python tools for VASP"
19
+ readme = "README.md"
20
+ requires-python = ">=3.8"
21
+ classifiers = [
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Unix Shell",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Operating System :: Unix",
26
+ "Intended Audience :: Science/Research",
27
+ "Topic :: Scientific/Engineering :: Chemistry",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/Tonner-Zech-Group/VASP-tools"
32
+ Issues = "https://github.com/Tonner-Zech-Group/VASP-tools/issues"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ from setuptools import setup
2
+ import glob
3
+ import site
4
+ import sys
5
+ site.ENABLE_USER_SITE = "--user" in sys.argv[1:]
6
+
7
+ with open("README.md", 'r') as f:
8
+ long_description = f.read()
9
+
10
+ scripts = []
11
+ for file in glob.glob('tools4vasp/*'):
12
+ if file.endswith('.py'):
13
+ scripts.append(file)
14
+ elif file.endswith('.sh'):
15
+ scripts.append(file)
16
+
17
+ setup(
18
+ name='tools4vasp',
19
+ version='1.0',
20
+ description='Python tools for VASP',
21
+ long_description=open('README.md').read(),
22
+ long_description_content_type='text/markdown',
23
+ author='Patrick Melix',
24
+ author_email='patrick.melix@uni-leipzig.de',
25
+ url="https://github.com/Tonner-Zech-Group/VASP-tools",
26
+ packages=['tools4vasp'],
27
+ install_requires=['ase', 'numpy', 'matplotlib', 'natsort', 'pymatgen'],
28
+ console_scripts = scripts
29
+ )
@@ -0,0 +1,3 @@
1
+ def test_dummy():
2
+ pass
3
+
File without changes
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ from ase import io
3
+ import numpy as np
4
+
5
+ poscar = io.read('POSCAR')
6
+
7
+ add = np.loadtxt('MODECAR')
8
+
9
+ poscar.write('poscar+modecar.xyz')
10
+
11
+ poscar.positions += add
12
+
13
+ poscar.write('poscar+modecar.xyz', append=True)
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Script to convert CHGCAR files to cube files and convert to e-/Ang^3
4
+ # by Patrick Melix
5
+ # 2022/04/04
6
+ #
7
+ # You can import the module and then call .main() or use it as a script
8
+ from pymatgen.io.vasp.outputs import Chgcar
9
+ from pymatgen.io.ase import AseAtomsAdaptor
10
+ from ase.io.cube import write_cube
11
+ import numpy as np
12
+ import os
13
+
14
+
15
+ def main(inFiles, outFiles, verbose=True, return_integrals=False, return_spin_integrals=False, mult_volume=False):
16
+ assert len(inFiles) == len(outFiles), "Number of input and output files must be equal!"
17
+ integrals = []
18
+ spin_integrals = []
19
+ for iFile,inFile in enumerate(inFiles):
20
+ if not os.path.isfile(inFile):
21
+ raise ValueError('File {:} does not exist'.format(inFile))
22
+
23
+ #if output exists mv to .bak
24
+ if os.path.isfile(outFiles[iFile]):
25
+ if verbose:
26
+ print('ATTENTION: {:} exists, moving to *.bak'.format(outFiles[iFile]))
27
+ os.rename(outFiles[iFile], outFiles[iFile]+'.bak')
28
+
29
+ if verbose:
30
+ print("Reading {}".format(inFile))
31
+ full_chgcar = Chgcar.from_file(inFile)
32
+ spinpol = 'diff' in full_chgcar.data.keys()
33
+ if return_spin_integrals and not spinpol:
34
+ raise ValueError("File {} is not spinpolarized!".format(inFile))
35
+ shape = full_chgcar.data['total'].shape
36
+ n_data = np.prod(shape)
37
+
38
+ if return_integrals:
39
+ integrals.append(np.sum(np.abs(full_chgcar.data['total'])))
40
+ integrals[-1] /= n_data
41
+ if return_spin_integrals:
42
+ spin_integrals.append(np.sum(np.abs(full_chgcar.data['diff'])))
43
+ spin_integrals[-1] /= n_data
44
+ if verbose:
45
+ print("Shape of data: {}".format(shape))
46
+ print("Total number of datapoints: {}".format(n_data))
47
+ if return_integrals:
48
+ integral = integrals[-1]
49
+ else:
50
+ integral = np.sum(np.abs(full_chgcar.data['total']))
51
+ integral /= n_data
52
+ print("Integral of total data is {}".format(integral))
53
+ if spinpol:
54
+ if return_spin_integrals:
55
+ spin_integral = spin_integrals[-1]
56
+ else:
57
+ spin_integral = np.sum(np.abs(full_chgcar.data['diff']))
58
+ spin_integral /= n_data
59
+ print("Integral of diff data is {}".format(spin_integral))
60
+
61
+ origin = np.zeros(3)
62
+ atoms = AseAtomsAdaptor.get_atoms(full_chgcar.structure)
63
+
64
+ #Contrary to VASP Wiki, the CHGCAR is not rho*V, but rho*n_data.
65
+ #So in order to have the integral over space = nelectrons, we need to divide by n_data.
66
+ #Since this would result in super small numbers, we can transform to rho*V
67
+ factor = n_data
68
+ if mult_volume:
69
+ factor /= atoms.get_volume()
70
+ full_chgcar.data['total'] /= factor
71
+ if spinpol:
72
+ full_chgcar.data['diff'] /= factor
73
+ #write cube
74
+ filename = "{}.cube".format(outFiles[iFile])
75
+ if verbose:
76
+ print("Writing {}".format(filename))
77
+ with open(filename, 'w') as f:
78
+ write_cube(f, atoms, data=full_chgcar.data['total'], origin=origin)
79
+ if spinpol:
80
+ filename = "{}_mag.cube".format(outFiles[iFile])
81
+ if verbose:
82
+ print("Writing {}".format(filename))
83
+ with open(filename, 'w') as f:
84
+ write_cube(f, atoms, data=full_chgcar.data['diff'], origin=origin)
85
+
86
+ if return_integrals:
87
+ if len(integrals) == 1:
88
+ if return_spin_integrals:
89
+ return integrals[0], spin_integrals[0]
90
+ else:
91
+ return integrals[0]
92
+ else:
93
+ if return_spin_integrals:
94
+ return integrals, spin_integrals
95
+ else:
96
+ return integrals
97
+ else:
98
+ return
99
+
100
+
101
+
102
+ if __name__ == "__main__":
103
+ import argparse
104
+ parser = argparse.ArgumentParser(description='Convert one or many CHGCAR-like files to cube format.')
105
+ parser.add_argument('input', type=str, nargs='+', help='Input Files')
106
+ parser.add_argument('-output', type=str, nargs='+', help='Output File Names (no extension)')
107
+ parser.add_argument('-v', help='Verbose', action='store_true')
108
+ parser.add_argument('--integral', help='Print Integrals', action='store_true')
109
+ parser.add_argument('--volume', help='Multiply the Density with the Cell Volume', action='store_true')
110
+ args = parser.parse_args()
111
+ main(args.input, args.output, verbose=args.v, return_integrals=args.integral, mult_volume=args.volume)
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Script to convert ELFCAR files to cube files
4
+ # by Patrick Melix
5
+ # 2022/04/04
6
+ #
7
+ # You can import the module and then call .main() or use it as a script
8
+ from pymatgen.io.vasp.outputs import Elfcar
9
+ from pymatgen.io.ase import AseAtomsAdaptor
10
+ from ase.io.cube import write_cube
11
+ import numpy as np
12
+ import os
13
+
14
+
15
+ def main(inFiles, outFiles, verbose=True, return_integrals=False, return_spin_integrals=False):
16
+ assert len(inFiles) == len(outFiles), "Number of input and output files must be equal!"
17
+ integrals = []
18
+ spin_integrals = []
19
+ for iFile, inFile in enumerate(inFiles):
20
+ if not os.path.isfile(inFile):
21
+ raise ValueError('File {:} does not exist'.format(inFile))
22
+
23
+ # if output exists mv to .bak
24
+ if os.path.isfile(outFiles[iFile]):
25
+ if verbose:
26
+ print('ATTENTION: {:} exists, moving to *.bak'.format(outFiles[iFile]))
27
+ os.rename(outFiles[iFile], outFiles[iFile]+'.bak')
28
+
29
+ if verbose:
30
+ print("Reading {}".format(inFile))
31
+ full_elfcar = Elfcar.from_file(inFile)
32
+ spinpol = 'diff' in full_elfcar.data.keys()
33
+ #pymatgen: “total” key refers to Spin.up, and “diff” refers to Spin.down.
34
+ if return_spin_integrals and not spinpol:
35
+ raise ValueError("File {} is not spinpolarized!".format(inFile))
36
+ shape = full_elfcar.data['total'].shape
37
+ n_data = np.prod(shape)
38
+
39
+ if spinpol:
40
+ full_data = full_elfcar.data['total'] + full_elfcar.data['diff']
41
+ else:
42
+ full_data = full_elfcar.data['total']
43
+
44
+ if return_integrals:
45
+ integrals.append(np.sum(np.abs(full_data)))
46
+ if return_spin_integrals:
47
+ spin_integrals.append((np.sum(np.abs(full_elfcar.data['total'])), np.sum(np.abs(full_elfcar.data['diff']))))
48
+ if verbose:
49
+ print("Shape of data: {}".format(shape))
50
+ print("Total number of datapoints: {}".format(n_data))
51
+ if return_integrals:
52
+ integral = integrals[-1]
53
+ else:
54
+ integral = np.sum(np.abs(full_data))
55
+ print("Integral of total data is {}".format(integral))
56
+ if spinpol:
57
+ if return_spin_integrals:
58
+ spin_integral = spin_integrals[-1]
59
+ else:
60
+ spin_integral = (np.sum(np.abs(full_elfcar.data['total'])), np.sum(np.abs(full_elfcar.data['diff'])))
61
+ print("Integral of spin data is up: {}, down: {}".format(*spin_integral))
62
+
63
+ origin = np.zeros(3)
64
+ atoms = AseAtomsAdaptor.get_atoms(full_elfcar.structure)
65
+
66
+ # write cubes
67
+ if spinpol:
68
+ filename = "{}_up.cube".format(outFiles[iFile])
69
+ if verbose:
70
+ print("Writing {}".format(filename))
71
+ with open(filename, 'w') as f:
72
+ write_cube(f, atoms, data=full_elfcar.data['total'], origin=origin)
73
+ filename = "{}_down.cube".format(outFiles[iFile])
74
+ if verbose:
75
+ print("Writing {}".format(filename))
76
+ with open(filename, 'w') as f:
77
+ write_cube(f, atoms, data=full_elfcar.data['diff'], origin=origin)
78
+ filename = "{}_diff.cube".format(outFiles[iFile])
79
+ if verbose:
80
+ print("Writing {}".format(filename))
81
+ with open(filename, 'w') as f:
82
+ write_cube(f, atoms, data=full_elfcar.data['total']-full_elfcar.data['diff'], origin=origin)
83
+ else:
84
+ filename = "{}.cube".format(outFiles[iFile])
85
+ if verbose:
86
+ print("Writing {}".format(filename))
87
+ with open(filename, 'w') as f:
88
+ write_cube(f, atoms, data=full_data, origin=origin)
89
+
90
+ if return_integrals:
91
+ if len(integrals) == 1:
92
+ if return_spin_integrals:
93
+ return integrals[0], spin_integrals[0]
94
+ else:
95
+ return integrals[0]
96
+ else:
97
+ if return_spin_integrals:
98
+ return integrals, spin_integrals
99
+ else:
100
+ return integrals
101
+ else:
102
+ return
103
+
104
+
105
+ if __name__ == "__main__":
106
+ import argparse
107
+ parser = argparse.ArgumentParser(description='Convert one or many ELFCAR files to cube format.')
108
+ parser.add_argument('input', type=str, nargs='+', help='Input Files')
109
+ parser.add_argument('-output', type=str, nargs='+', help='Output File Names (no extension)')
110
+ parser.add_argument('-v', help='Verbose', action='store_true')
111
+ args = parser.parse_args()
112
+ main(args.input, args.output, verbose=args.v)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python3
2
+ from ase.calculators.vasp import Vasp
3
+ import os
4
+ os.environ['VASP_PP_PATH'] = "/home/patrickm/lib/vasp/ase"
5
+
6
+ calc = Vasp(restart=True, directory='./')
7
+ vibs = calc.get_vibrations()
8
+ vibs.write_jmol()
9
+
10
+ energies = vibs.get_energies()
11
+ nVibs = len(energies)
12
+ for i in range(nVibs):
13
+ with open("vib-{:03d}.xyz".format(i+1), 'w') as f:
14
+ for frame in vibs.iter_animated_mode(i):
15
+ frame.write(f, format='extxyz')
16
+ print("Frequency #{:03d}: {:10.6f} cm-1".format(i+1, energies[i]))
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import math
4
+ from ase.data import atomic_masses
5
+ import ase.io
6
+ from ase.units import _amu as amu, _me as me
7
+
8
+
9
+ def get_atomic_mass(element_symbol):
10
+ mass_in_amu = atomic_masses[element_symbol]
11
+ # Convert mass to atomic units (1 amu = 1.66053904e-27 kg)
12
+ mass_in_au = mass_in_amu * amu / me
13
+ return mass_in_au
14
+
15
+
16
+ def getAtomsFromOutcar(outcar_file):
17
+ atoms = ase.io.read(outcar_file)
18
+ return atoms
19
+
20
+
21
+ def get_frequencies(outcar):
22
+ freqLines = []
23
+ for line in outcar:
24
+ if " f/i=" in line:
25
+ freqLines.append(line)
26
+ if "Eigenvectors after division by SQRT(mass)" in line:
27
+ break
28
+ return freqLines
29
+
30
+
31
+ def generate_mw(frequency, atoms):
32
+ weights = atoms.get_masses()
33
+ freq_mw = []
34
+ for i in range(len(frequency)):
35
+ freq_line = frequency[i]
36
+ factor = math.sqrt(weights[i])
37
+ new_freq_line = []
38
+ for freq in freq_line:
39
+ if not freq == 0:
40
+ new_freq_line.append(freq / factor)
41
+ else:
42
+ new_freq_line.append(0.0)
43
+ freq_mw.append(new_freq_line)
44
+ return freq_mw
45
+
46
+
47
+ def write_modecar(frequency, filename):
48
+ filestring = ""
49
+ for freq_line in frequency:
50
+ line = ""
51
+ for freq in freq_line:
52
+ if freq >= 0:
53
+ line += " "
54
+ else:
55
+ line += " "
56
+ line += "{:.10E}".format(freq)
57
+ filestring += line + "\n"
58
+ with open(filename, "w") as file:
59
+ file.write(filestring)
60
+
61
+
62
+ def read_frequency_from_outcar(outcar, freq_line, atoms):
63
+ current_frequency = []
64
+ start_index = outcar.index(freq_line) + 2
65
+ end_index = start_index + 0 + len(atoms)
66
+ for line in outcar[start_index:end_index]:
67
+ vals = line.split()
68
+ current_frequency.append([float(vals[3]), float(vals[4]), float(vals[5])])
69
+ return current_frequency
70
+
71
+
72
+ if __name__ == "__main__":
73
+ parser = argparse.ArgumentParser(
74
+ prog="freq2mode",
75
+ description="A VASP tool, which generates MODECAR and mass-weighted MODCAR files from frequency calculations",
76
+ epilog=""
77
+ )
78
+ parser.add_argument("-o", "--outcar", type=str, help="Optional: specify OUTCAR file", required=False, default="OUTCAR")
79
+ parser.add_argument("-i", "--index", type=int, help="Force non interactive mode by specifying the index of the imaginary frequency to use", required=False, default=-1)
80
+ args = parser.parse_args()
81
+ outcar_file = args.outcar
82
+ with open(outcar_file, "r") as f:
83
+ outcar = f.readlines()
84
+ atoms = getAtomsFromOutcar(outcar_file)
85
+ freqs = get_frequencies(outcar)
86
+ if len(freqs) == 0:
87
+ print("No imaginary frequencies found")
88
+ exit(0)
89
+ if len(freqs) < args.index + 1:
90
+ print("The specified mode was not found in the OUTCAR file")
91
+ exit(1)
92
+ if len(freqs) > 1:
93
+ print(f"Found {len(freqs)} imaginary frequencies. Select the frequency for MODECAR generation")
94
+ freq_index = 0
95
+ for freq in freqs:
96
+ print(f"{freq_index} {freq}")
97
+ freq_index += 1
98
+ if args.index == -1:
99
+ freq_index = int(input("Frequency for MODECAR generation:"))
100
+ else:
101
+ print(f"Using Frequency {args.index}")
102
+ freq_index = args.index
103
+ else:
104
+ print("Found one imaginary mode:")
105
+ print(freqs[0])
106
+ print("Using this mode for MODECAR generation")
107
+ freq_index = 0
108
+ frequency = read_frequency_from_outcar(outcar, freqs[freq_index], atoms)
109
+ print("Writing MODECAR file")
110
+ write_modecar(frequency, "MODECAR")
111
+ print("Writing mass-weighted MODECAR file")
112
+ frequency_mw = generate_mw(frequency, atoms)
113
+ write_modecar(frequency_mw, "MODECAR.MW")
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Script to get a KSPACING from a KPOINTS file and a POSCAR
4
+ # by Patrick Melix
5
+ # 2023/05/22
6
+ #
7
+
8
+
9
+ def get_kspacing():
10
+ from ase import io
11
+ import numpy as np
12
+ mol = io.read('POSCAR')
13
+ cellparams = mol.cell.cellpar()
14
+ r_cell = mol.cell.reciprocal()
15
+ print("The cell parameters are |a|={} |b|={} |c|={} alpha={} beta={} gamma={}".format(*[ round(x,2) for x in cellparams]))
16
+ print("The reciprocal cell is: {}".format(r_cell))
17
+ with open('KPOINTS', 'r') as f:
18
+ kpoints = f.readlines()
19
+ if kpoints[1].strip() != "0":
20
+ raise ValueError("KPOINTS file does not use Automatic Scheme, but only this is supported!")
21
+ kgrid = [ int(k) for k in kpoints[3].split() ]
22
+ assert len(kgrid) == 3, "Expected to find 3 integers in line 4 of KPOINTS file!"
23
+ print("The KGRID from KPOINTS is: {} {} {}".format(*kgrid))
24
+ kspacing = [ np.linalg.norm(r_cell[i])*2*np.pi/kgrid[i] for i in range(3) ]
25
+ #kgrid = [ int(max(1, np.ceil(np.linalg.norm(r_cell[i])*2*np.pi/kspacing))) for i in range(3)]
26
+ print("The corresponding KSPACING for KGRID {} {} {} is {}Å^-1, {}Å^-1, {}Å^-1".format(*kgrid, *[ round(k, 3) for k in kspacing ]))
27
+
28
+
29
+ if __name__ == "__main__":
30
+ import argparse
31
+ parser = argparse.ArgumentParser(
32
+ description='Get a KSPACING from current POSCAR and KPOINTS')
33
+ args = parser.parse_args()
34
+ get_kspacing()
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ # Script to get a KGrid from a KSPACING value and a POSCAR
4
+ # by Patrick Melix
5
+ # 2023/05/22
6
+ #
7
+
8
+
9
+ def get_kgrid(kspacing):
10
+ from ase import io
11
+ import numpy as np
12
+ mol = io.read('POSCAR')
13
+ cellparams = mol.cell.cellpar()
14
+ r_cell = mol.cell.reciprocal()
15
+ print("The cell parameters are |a|={} |b|={} |c|={} alpha={} beta={} gamma={}".format(*[ round(x,2) for x in cellparams]))
16
+ print("The reciprocal cell is: {}".format(r_cell))
17
+ kgrid = [ int(max(1, np.ceil(np.linalg.norm(r_cell[i])*2*np.pi/kspacing))) for i in range(3)]
18
+ print("The corresponding KGRID for KSPACING {}Å^-1 is: {} {} {}".format(kspacing, *kgrid))
19
+
20
+
21
+ if __name__ == "__main__":
22
+ import argparse
23
+ parser = argparse.ArgumentParser(
24
+ description='Get a KGrid from current POSCAR and a KSPACING value')
25
+ parser.add_argument('kspacing', type=float, help='KSPACING value')
26
+ args = parser.parse_args()
27
+ get_kgrid(args.kspacing)