Coverage for /builds/hweiske/ase/ase/calculators/onetep.py: 37.78%

45 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-22 11:22 +0000

1"""ONETEP interface for the Atomic Simulation Environment (ASE) package 

2 

3T. Demeyere, T.Demeyere@soton.ac.uk (2023) 

4 

5https://onetep.org""" 

6 

7from os import environ 

8 

9from ase.calculators.genericfileio import (BaseProfile, CalculatorTemplate, 

10 GenericFileIOCalculator, 

11 read_stdout) 

12from ase.io import read, write 

13 

14 

15class OnetepProfile(BaseProfile): 

16 """ 

17 ONETEP profile class, additional "old" parameter 

18 is automatically passed for now if the user uses the 

19 now deprecated "ASE_ONETEP_COMMAND". 

20 """ 

21 

22 def __init__(self, binary, old=False, **kwargs): 

23 """ 

24 Parameters 

25 ---------- 

26 binary: str 

27 Path to the ONETEP binary. 

28 old: bool 

29 If True, will use the old ASE_ONETEP_COMMAND 

30 interface. 

31 **kwargs: dict 

32 Additional kwargs are passed to the BaseProfile 

33 class. 

34 """ 

35 super().__init__(**kwargs) 

36 self.binary = binary 

37 self.old = old 

38 

39 def version(self): 

40 lines = read_stdout(self.binary) 

41 return self.parse_version(lines) 

42 

43 def parse_version(lines): 

44 return '1.0.0' 

45 

46 def get_calculator_command(self, inputfile): 

47 if self.old: 

48 return self.binary.split() + [str(inputfile)] 

49 else: 

50 return [self.binary, str(inputfile)] 

51 

52 

53class OnetepTemplate(CalculatorTemplate): 

54 _label = 'onetep' 

55 

56 def __init__(self, append): 

57 super().__init__( 

58 'ONETEP', 

59 implemented_properties=[ 

60 'energy', 

61 'free_energy', 

62 'forces', 

63 'stress']) 

64 self.inputname = f'{self._label}.dat' 

65 self.outputname = f'{self._label}.out' 

66 self.errorname = f'{self._label}.err' 

67 self.append = append 

68 

69 def execute(self, directory, profile): 

70 profile.run(directory, self.inputname, self.outputname, 

71 self.errorname, append=self.append) 

72 

73 def read_results(self, directory): 

74 output_path = directory / self.outputname 

75 atoms = read(output_path, format='onetep-out') 

76 return dict(atoms.calc.properties()) 

77 

78 def write_input(self, profile, directory, atoms, parameters, properties): 

79 input_path = directory / self.inputname 

80 write(input_path, atoms, format='onetep-in', 

81 properties=properties, **parameters) 

82 

83 def load_profile(self, cfg, **kwargs): 

84 return OnetepProfile.from_config(cfg, self.name, **kwargs) 

85 

86 

87class Onetep(GenericFileIOCalculator): 

88 """ 

89 Class for the ONETEP calculator, uses ase/io/onetep.py. 

90 Need the env variable "ASE_ONETEP_COMMAND" defined to 

91 properly work. All other options are passed in kwargs. 

92 

93 Parameters 

94 ---------- 

95 autorestart : Bool 

96 When activated, manages restart keywords automatically. 

97 append: Bool 

98 Append to output instead of overwriting. 

99 directory: str 

100 Directory where to run the calculation(s). 

101 keywords: dict 

102 Dictionary with ONETEP keywords to write, 

103 keywords with lists as values will be 

104 treated like blocks, with each element 

105 of list being a different line. 

106 xc: str 

107 DFT xc to use e.g (PBE, RPBE, ...). 

108 ngwfs_count: int|list|dict 

109 Behaviour depends on the type: 

110 int: every species will have this amount 

111 of ngwfs. 

112 list: list of int, will be attributed 

113 alphabetically to species: 

114 dict: keys are species name(s), 

115 value are their number: 

116 ngwfs_radius: int|list|dict 

117 Behaviour depends on the type: 

118 float: every species will have this radius. 

119 list: list of float, will be attributed 

120 alphabetically to species: 

121 [10.0, 9.0] 

122 dict: keys are species name(s), 

123 value are their radius: 

124 {'Na': 9.0, 'Cl': 10.0} 

125 pseudopotentials: list|dict 

126 Behaviour depends on the type: 

127 list: list of string(s), will be attributed 

128 alphabetically to specie(s): 

129 ['Cl.usp', 'Na.usp'] 

130 dict: keys are species name(s) their 

131 value are the pseudopotential file to use: 

132 {'Na': 'Na.usp', 'Cl': 'Cl.usp'} 

133 pseudo_path: str 

134 Where to look for pseudopotential, correspond 

135 to the pseudo_path keyword of ONETEP. 

136 

137 .. note:: 

138 write_forces is always turned on by default 

139 when using this interface. 

140 

141 .. note:: 

142 Little to no check is performed on the keywords provided by the user 

143 via the keyword dictionary, it is the user responsibility that they 

144 are valid ONETEP keywords. 

145 """ 

146 

147 def __init__( 

148 self, 

149 *, 

150 profile=None, 

151 directory='.', 

152 parallel_info=None, 

153 parallel=True, 

154 **kwargs): 

155 

156 self.keywords = kwargs.get('keywords', None) 

157 self.template = OnetepTemplate( 

158 append=kwargs.pop('append', False) 

159 ) 

160 

161 if 'ASE_ONETEP_COMMAND' in environ and profile is None: 

162 import warnings 

163 warnings.warn("using ASE_ONETEP_COMMAND env is \ 

164 deprecated, please use OnetepProfile", 

165 FutureWarning) 

166 profile = OnetepProfile(environ['ASE_ONETEP_COMMAND'], old=True) 

167 

168 super().__init__(profile=profile, template=self.template, 

169 directory=directory, 

170 parameters=kwargs, 

171 parallel=parallel, 

172 parallel_info=parallel_info)