Coverage for /builds/hweiske/ase/ase/calculators/abinit.py: 94.23%

52 statements  

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

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

2 

3http://www.abinit.org/ 

4""" 

5 

6from pathlib import Path 

7from subprocess import check_output 

8 

9import ase.io.abinit as io 

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

11 GenericFileIOCalculator) 

12 

13 

14class AbinitProfile(BaseProfile): 

15 def __init__(self, binary, *, pp_paths=None, **kwargs): 

16 super().__init__(**kwargs) 

17 self.binary = binary 

18 # XXX pp_paths is a raw configstring when it gets here. 

19 # All the config stuff should have been loaded somehow by now, 

20 # so this should be refactored.º 

21 if isinstance(pp_paths, str): 

22 pp_paths = [path for path in pp_paths.splitlines() if path] 

23 if pp_paths is None: 

24 pp_paths = [] 

25 self.pp_paths = pp_paths 

26 

27 def version(self): 

28 argv = [self.binary, '--version'] 

29 return check_output(argv, encoding='ascii').strip() 

30 

31 def get_calculator_command(self, inputfile): 

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

33 

34 def socketio_argv_unix(self, socket): 

35 # XXX clean up the passing of the inputfile 

36 inputfile = AbinitTemplate().input_file 

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

38 

39 

40class AbinitTemplate(CalculatorTemplate): 

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

42 

43 def __init__(self): 

44 super().__init__( 

45 name='abinit', 

46 implemented_properties=[ 

47 'energy', 

48 'free_energy', 

49 'forces', 

50 'stress', 

51 'magmom', 

52 ], 

53 ) 

54 

55 # XXX superclass should require inputname and outputname 

56 

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

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

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

60 

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

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

63 errorfile=self.errorname) 

64 

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

66 directory = Path(directory) 

67 parameters = dict(parameters) 

68 pp_paths = parameters.pop('pp_paths', profile.pp_paths) 

69 assert pp_paths is not None 

70 

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

72 kw.update(parameters) 

73 

74 io.prepare_abinit_input( 

75 directory=directory, 

76 atoms=atoms, 

77 properties=properties, 

78 parameters=kw, 

79 pp_paths=pp_paths, 

80 ) 

81 

82 def read_results(self, directory): 

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

84 

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

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

87 

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

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

90 # calculators, should refactor if needed yet again 

91 if unixsocket: 

92 ipi_arg = f'{unixsocket}:UNIX' 

93 else: 

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

95 

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

97 '--ipi', 

98 ipi_arg, 

99 ] 

100 

101 def socketio_parameters(self, unixsocket, port): 

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

103 

104 

105class Abinit(GenericFileIOCalculator): 

106 """Class for doing ABINIT calculations. 

107 

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

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

110 

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

112 """ 

113 

114 def __init__( 

115 self, 

116 *, 

117 profile=None, 

118 directory='.', 

119 parallel_info=None, 

120 parallel=True, 

121 **kwargs, 

122 ): 

123 """Construct ABINIT-calculator object. 

124 

125 Parameters 

126 ========== 

127 label: str 

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

129 Default is 'abinit'. 

130 

131 Examples 

132 ======== 

133 Use default values: 

134 

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

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

137 >>> e = h.get_potential_energy() 

138 

139 """ 

140 

141 super().__init__( 

142 template=AbinitTemplate(), 

143 profile=profile, 

144 directory=directory, 

145 parallel_info=parallel_info, 

146 parallel=parallel, 

147 parameters=kwargs, 

148 )