Hide keyboard shortcuts

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 

2 

3from ase.calculators.calculator import Calculator 

4from ase.io import read 

5 

6from .create_input import GenerateVaspInput 

7 

8import time 

9import os 

10import sys 

11 

12 

13class VaspInteractive(GenerateVaspInput, Calculator): # type: ignore 

14 name = "VaspInteractive" 

15 implemented_properties = ['energy', 'forces', 'stress'] 

16 

17 mandatory_input = {'potim': 0.0, 

18 'ibrion': -1, 

19 'interactive': True, 

20 } 

21 

22 default_input = {'nsw': 2000, 

23 } 

24 

25 def __init__(self, txt="interactive.log", print_log=False, process=None, 

26 command=None, path="./", **kwargs): 

27 

28 GenerateVaspInput.__init__(self) 

29 

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) 

37 

38 for kw, val in self.default_input.items(): 

39 if kw not in kwargs: 

40 kwargs[kw] = val 

41 

42 self.set(**kwargs) 

43 

44 self.process = process 

45 self.path = path 

46 

47 if txt is not None: 

48 self.txt = open(txt, "a") 

49 else: 

50 self.txt = None 

51 self.print_log = print_log 

52 

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') 

62 

63 if isinstance(self.command, str): 

64 self.command = self.command.split() 

65 

66 self.atoms = None 

67 

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() 

76 

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="") 

82 

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))) 

103 

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 

109 

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. 

113 

114 raise RuntimeError("VASP exited unexpectedly with exit code {}" 

115 "".format(self.subprocess.poll())) 

116 

117 def close(self): 

118 if self.process is None: 

119 return 

120 

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.') 

124 

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 

131 

132 def calculate(self, atoms=None, properties=['energy'], 

133 system_changes=['positions', 'numbers', 'cell']): 

134 Calculator.calculate(self, atoms, properties, system_changes) 

135 

136 if not system_changes: 

137 return 

138 

139 if 'numbers' in system_changes: 

140 self.close() 

141 

142 self._run_vasp(atoms) 

143 

144 new = read(os.path.join(self.path, 'vasprun.xml'), index=-1) 

145 

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()} 

150 

151 def __del__(self): 

152 self.close()