Coverage for /builds/alexhroom/ase/ase/calculators/onetep.py: 41.86%

43 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2024-08-05 14:37 +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 copy import deepcopy 

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. 

18 """ 

19 

20 configvars = {'pseudo_path'} 

21 

22 def __init__(self, command, pseudo_path, **kwargs): 

23 """ 

24 Parameters 

25 ---------- 

26 command: str 

27 The onetep command (not including inputfile). 

28 **kwargs: dict 

29 Additional kwargs are passed to the BaseProfile 

30 class. 

31 """ 

32 super().__init__(command, **kwargs) 

33 self.pseudo_path = pseudo_path 

34 

35 def version(self): 

36 lines = read_stdout(self._split_command) 

37 return self.parse_version(lines) 

38 

39 def parse_version(lines): 

40 return '1.0.0' 

41 

42 def get_calculator_command(self, inputfile): 

43 return [str(inputfile)] 

44 

45 

46class OnetepTemplate(CalculatorTemplate): 

47 _label = 'onetep' 

48 

49 def __init__(self, append): 

50 super().__init__( 

51 'ONETEP', 

52 implemented_properties=[ 

53 'energy', 

54 'free_energy', 

55 'forces', 

56 'stress']) 

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

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

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

60 self.append = append 

61 

62 def execute(self, directory, profile): 

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

64 self.errorname, append=self.append) 

65 

66 def read_results(self, directory): 

67 output_path = directory / self.outputname 

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

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

70 

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

72 input_path = directory / self.inputname 

73 

74 parameters = deepcopy(parameters) 

75 

76 keywords = parameters.get('keywords', {}) 

77 keywords.setdefault('pseudo_path', profile.pseudo_path) 

78 parameters['keywords'] = keywords 

79 

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 **kwargs): 

153 

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

155 self.template = OnetepTemplate( 

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

157 ) 

158 

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

160 directory=directory, 

161 parameters=kwargs)