Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from subprocess import Popen, PIPE
3from ase.calculators.calculator import Calculator
4from ase.io import read
6from .create_input import GenerateVaspInput
8import time
9import os
10import sys
13class VaspInteractive(GenerateVaspInput, Calculator): # type: ignore
14 name = "VaspInteractive"
15 implemented_properties = ['energy', 'forces', 'stress']
17 mandatory_input = {'potim': 0.0,
18 'ibrion': -1,
19 'interactive': True,
20 }
22 default_input = {'nsw': 2000,
23 }
25 def __init__(self, txt="interactive.log", print_log=False, process=None,
26 command=None, path="./", **kwargs):
28 GenerateVaspInput.__init__(self)
30 for kw, val in self.mandatory_input.items():
31 if kw in kwargs and val != kwargs[kw]:
32 raise ValueError('Keyword {} cannot be overridden! '
33 'It must have have value {}, but {} '
34 'was provided instead.'.format(kw, val,
35 kwargs[kw]))
36 kwargs.update(self.mandatory_input)
38 for kw, val in self.default_input.items():
39 if kw not in kwargs:
40 kwargs[kw] = val
42 self.set(**kwargs)
44 self.process = process
45 self.path = path
47 if txt is not None:
48 self.txt = open(txt, "a")
49 else:
50 self.txt = None
51 self.print_log = print_log
53 if command is not None:
54 self.command = command
55 elif 'VASP_COMMAND' in os.environ:
56 self.command = os.environ['VASP_COMMAND']
57 elif 'VASP_SCRIPT' in os.environ:
58 self.command = os.environ['VASP_SCRIPT']
59 else:
60 raise RuntimeError('Please set either command in calculator'
61 ' or VASP_COMMAND environment variable')
63 if isinstance(self.command, str):
64 self.command = self.command.split()
66 self.atoms = None
68 def _stdin(self, text, ending="\n"):
69 if self.txt is not None:
70 self.txt.write(text + ending)
71 if self.print_log:
72 print(text, end=ending)
73 self.process.stdin.write(text + ending)
74 if sys.version_info[0] >= 3:
75 self.process.stdin.flush()
77 def _stdout(self, text):
78 if self.txt is not None:
79 self.txt.write(text)
80 if self.print_log:
81 print(text, end="")
83 def _run_vasp(self, atoms):
84 if self.process is None:
85 stopcar = os.path.join(self.path, 'STOPCAR')
86 if os.path.isfile(stopcar):
87 os.remove(stopcar)
88 self._stdout("Writing VASP input files\n")
89 self.initialize(atoms)
90 self.write_input(atoms, directory=self.path)
91 self._stdout("Starting VASP for initial step...\n")
92 if sys.version_info[0] >= 3:
93 self.process = Popen(self.command, stdout=PIPE,
94 stdin=PIPE, stderr=PIPE, cwd=self.path,
95 universal_newlines=True)
96 else:
97 self.process = Popen(self.command, stdout=PIPE,
98 stdin=PIPE, stderr=PIPE, cwd=self.path)
99 else:
100 self._stdout("Inputting positions...\n")
101 for atom in atoms.get_scaled_positions():
102 self._stdin(' '.join(map('{:19.16f}'.format, atom)))
104 while self.process.poll() is None:
105 text = self.process.stdout.readline()
106 self._stdout(text)
107 if "POSITIONS: reading from stdin" in text:
108 return
110 # If we've reached this point, then VASP has exited without asking for
111 # new positions, meaning it either exited without error unexpectedly,
112 # or it exited with an error. Either way, we need to raise an error.
114 raise RuntimeError("VASP exited unexpectedly with exit code {}"
115 "".format(self.subprocess.poll()))
117 def close(self):
118 if self.process is None:
119 return
121 self._stdout('Attemping to close VASP cleanly\n')
122 with open(os.path.join(self.path, 'STOPCAR'), 'w') as stopcar:
123 stopcar.write('LABORT = .TRUE.')
125 self._run_vasp(self.atoms)
126 self._run_vasp(self.atoms)
127 while self.process.poll() is None:
128 time.sleep(1)
129 self._stdout("VASP has been closed\n")
130 self.process = None
132 def calculate(self, atoms=None, properties=['energy'],
133 system_changes=['positions', 'numbers', 'cell']):
134 Calculator.calculate(self, atoms, properties, system_changes)
136 if not system_changes:
137 return
139 if 'numbers' in system_changes:
140 self.close()
142 self._run_vasp(atoms)
144 new = read(os.path.join(self.path, 'vasprun.xml'), index=-1)
146 self.results = {'free_energy': new.get_potential_energy(force_consistent=True),
147 'energy': new.get_potential_energy(),
148 'forces': new.get_forces()[self.resort],
149 'stress': new.get_stress()}
151 def __del__(self):
152 self.close()