Coverage for /builds/debichem-team/python-ase/ase/calculators/onetep.py: 41.86%

43 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-03-06 04:00 +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 ( 

10 BaseProfile, 

11 CalculatorTemplate, 

12 GenericFileIOCalculator, 

13 read_stdout, 

14) 

15from ase.io import read, write 

16 

17 

18class OnetepProfile(BaseProfile): 

19 """ 

20 ONETEP profile class. 

21 """ 

22 

23 configvars = {'pseudo_path'} 

24 

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

26 """ 

27 Parameters 

28 ---------- 

29 command: str 

30 The onetep command (not including inputfile). 

31 **kwargs: dict 

32 Additional kwargs are passed to the BaseProfile 

33 class. 

34 """ 

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

36 self.pseudo_path = pseudo_path 

37 

38 def version(self): 

39 lines = read_stdout(self._split_command) 

40 return self.parse_version(lines) 

41 

42 def parse_version(lines): 

43 return '1.0.0' 

44 

45 def get_calculator_command(self, inputfile): 

46 return [str(inputfile)] 

47 

48 

49class OnetepTemplate(CalculatorTemplate): 

50 _label = 'onetep' 

51 

52 def __init__(self, append): 

53 super().__init__( 

54 'ONETEP', 

55 implemented_properties=[ 

56 'energy', 

57 'free_energy', 

58 'forces', 

59 'stress']) 

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

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

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

63 self.append = append 

64 

65 def execute(self, directory, profile): 

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

67 self.errorname, append=self.append) 

68 

69 def read_results(self, directory): 

70 output_path = directory / self.outputname 

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

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

73 

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

75 input_path = directory / self.inputname 

76 

77 parameters = deepcopy(parameters) 

78 

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

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

81 parameters['keywords'] = keywords 

82 

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

84 properties=properties, **parameters) 

85 

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

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

88 

89 

90class Onetep(GenericFileIOCalculator): 

91 """ 

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

93 Need the env variable "ASE_ONETEP_COMMAND" defined to 

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

95 

96 Parameters 

97 ---------- 

98 autorestart : Bool 

99 When activated, manages restart keywords automatically. 

100 append: Bool 

101 Append to output instead of overwriting. 

102 directory: str 

103 Directory where to run the calculation(s). 

104 keywords: dict 

105 Dictionary with ONETEP keywords to write, 

106 keywords with lists as values will be 

107 treated like blocks, with each element 

108 of list being a different line. 

109 xc: str 

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

111 ngwfs_count: int|list|dict 

112 Behaviour depends on the type: 

113 int: every species will have this amount 

114 of ngwfs. 

115 list: list of int, will be attributed 

116 alphabetically to species: 

117 dict: keys are species name(s), 

118 value are their number: 

119 ngwfs_radius: int|list|dict 

120 Behaviour depends on the type: 

121 float: every species will have this radius. 

122 list: list of float, will be attributed 

123 alphabetically to species: 

124 [10.0, 9.0] 

125 dict: keys are species name(s), 

126 value are their radius: 

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

128 pseudopotentials: list|dict 

129 Behaviour depends on the type: 

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

131 alphabetically to specie(s): 

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

133 dict: keys are species name(s) their 

134 value are the pseudopotential file to use: 

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

136 pseudo_path: str 

137 Where to look for pseudopotential, correspond 

138 to the pseudo_path keyword of ONETEP. 

139 

140 .. note:: 

141 write_forces is always turned on by default 

142 when using this interface. 

143 

144 .. note:: 

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

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

147 are valid ONETEP keywords. 

148 """ 

149 

150 def __init__( 

151 self, 

152 *, 

153 profile=None, 

154 directory='.', 

155 **kwargs): 

156 

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

158 self.template = OnetepTemplate( 

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

160 ) 

161 

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

163 directory=directory, 

164 parameters=kwargs)