Coverage for /builds/kinetik161/ase/ase/calculators/abinit.py: 90.38%

52 statements  

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

1"""This module defines an ASE interface to ABINIT. 

2 

3http://www.abinit.org/ 

4""" 

5 

6import re 

7from pathlib import Path 

8from subprocess import check_output 

9 

10import ase.io.abinit as io 

11from ase.calculators.genericfileio import ( 

12 CalculatorTemplate, 

13 GenericFileIOCalculator, 

14 BaseProfile, 

15) 

16 

17 

18def get_abinit_version(command): 

19 txt = check_output([command, '--version']).decode('ascii') 

20 # This allows trailing stuff like betas, rc and so 

21 m = re.match(r'\s*(\d\.\d\.\d)', txt) 

22 if m is None: 

23 raise RuntimeError( 

24 'Cannot recognize abinit version. ' 'Start of output: {}'.format( 

25 txt[:40] 

26 ) 

27 ) 

28 return m.group(1) 

29 

30 

31class AbinitProfile(BaseProfile): 

32 def __init__(self, binary, **kwargs): 

33 super().__init__(**kwargs) 

34 self.binary = binary 

35 

36 def version(self): 

37 return check_output( 

38 self.binary + ['--version'], encoding='ascii' 

39 ).strip() 

40 

41 def get_calculator_command(self, inputfile): 

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

43 

44 def socketio_argv_unix(self, socket): 

45 # XXX clean up the passing of the inputfile 

46 inputfile = AbinitTemplate().input_file 

47 return [self.binary, inputfile, '--ipi', f'{socket}:UNIX'] 

48 

49 

50class AbinitTemplate(CalculatorTemplate): 

51 _label = 'abinit' # Controls naming of files within calculation directory 

52 

53 def __init__(self): 

54 super().__init__( 

55 name='abinit', 

56 implemented_properties=[ 

57 'energy', 

58 'free_energy', 

59 'forces', 

60 'stress', 

61 'magmom', 

62 ], 

63 ) 

64 

65 # XXX superclass should require inputname and outputname 

66 

67 self.inputname = f'{self._label}.in' 

68 self.outputname = f'{self._label}.log' 

69 

70 def execute(self, directory, profile) -> None: 

71 profile.run(directory, self.inputname, self.outputname) 

72 

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

74 directory = Path(directory) 

75 parameters = dict(parameters) 

76 pp_paths = parameters.pop('pp_paths', None) 

77 assert pp_paths is not None 

78 

79 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi') 

80 kw.update(parameters) 

81 

82 io.prepare_abinit_input( 

83 directory=directory, 

84 atoms=atoms, 

85 properties=properties, 

86 parameters=kw, 

87 pp_paths=pp_paths, 

88 ) 

89 

90 def read_results(self, directory): 

91 return io.read_abinit_outputs(directory, self._label) 

92 

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

94 return AbinitProfile.from_config(cfg, self.name, **kwargs) 

95 

96 def socketio_argv(self, profile, unixsocket, port): 

97 # XXX This handling of --ipi argument is used by at least two 

98 # calculators, should refactor if needed yet again 

99 if unixsocket: 

100 ipi_arg = f'{unixsocket}:UNIX' 

101 else: 

102 ipi_arg = f'localhost:{port:d}' 

103 

104 return profile.get_calculator_command(self.inputname) + [ 

105 '--ipi', 

106 ipi_arg, 

107 ] 

108 

109 def socketio_parameters(self, unixsocket, port): 

110 return dict(ionmov=28, expert_user=1, optcell=2) 

111 

112 

113class Abinit(GenericFileIOCalculator): 

114 """Class for doing ABINIT calculations. 

115 

116 The default parameters are very close to those that the ABINIT 

117 Fortran code would use. These are the exceptions:: 

118 

119 calc = Abinit(label='abinit', xc='LDA', ecut=400, toldfe=1e-5) 

120 """ 

121 

122 def __init__( 

123 self, 

124 *, 

125 profile=None, 

126 directory='.', 

127 parallel_info=None, 

128 parallel=True, 

129 **kwargs, 

130 ): 

131 """Construct ABINIT-calculator object. 

132 

133 Parameters 

134 ========== 

135 label: str 

136 Prefix to use for filenames (label.in, label.txt, ...). 

137 Default is 'abinit'. 

138 

139 Examples 

140 ======== 

141 Use default values: 

142 

143 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001)) 

144 >>> h.center(vacuum=3.0) 

145 >>> e = h.get_potential_energy() 

146 

147 """ 

148 

149 super().__init__( 

150 template=AbinitTemplate(), 

151 profile=profile, 

152 directory=directory, 

153 parallel_info=parallel_info, 

154 parallel=parallel, 

155 parameters=kwargs, 

156 )