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

52 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2024-08-05 14:37 +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 configvars = {'pp_paths'} 

16 

17 def __init__(self, command, *, pp_paths=None, **kwargs): 

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

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

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

21 # so this should be refactored. 

22 if isinstance(pp_paths, str): 

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

24 if pp_paths is None: 

25 pp_paths = [] 

26 self.pp_paths = pp_paths 

27 

28 def version(self): 

29 argv = [*self._split_command, '--version'] 

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

31 

32 def get_calculator_command(self, inputfile): 

33 return [str(inputfile)] 

34 

35 def socketio_argv_unix(self, socket): 

36 # XXX clean up the passing of the inputfile 

37 inputfile = AbinitTemplate().input_file 

38 return [inputfile, '--ipi', f'{socket}:UNIX'] 

39 

40 

41class AbinitTemplate(CalculatorTemplate): 

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

43 

44 def __init__(self): 

45 super().__init__( 

46 name='abinit', 

47 implemented_properties=[ 

48 'energy', 

49 'free_energy', 

50 'forces', 

51 'stress', 

52 'magmom', 

53 ], 

54 ) 

55 

56 # XXX superclass should require inputname and outputname 

57 

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

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

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

61 

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

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

64 errorfile=self.errorname) 

65 

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

67 directory = Path(directory) 

68 parameters = dict(parameters) 

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

70 assert pp_paths is not None 

71 

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

73 kw.update(parameters) 

74 

75 io.prepare_abinit_input( 

76 directory=directory, 

77 atoms=atoms, 

78 properties=properties, 

79 parameters=kw, 

80 pp_paths=pp_paths, 

81 ) 

82 

83 def read_results(self, directory): 

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

85 

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

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

88 

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

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

91 # calculators, should refactor if needed yet again 

92 if unixsocket: 

93 ipi_arg = f'{unixsocket}:UNIX' 

94 else: 

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

96 

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

98 '--ipi', 

99 ipi_arg, 

100 ] 

101 

102 def socketio_parameters(self, unixsocket, port): 

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

104 

105 

106class Abinit(GenericFileIOCalculator): 

107 """Class for doing ABINIT calculations. 

108 

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

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

111 

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

113 """ 

114 

115 def __init__( 

116 self, 

117 *, 

118 profile=None, 

119 directory='.', 

120 **kwargs, 

121 ): 

122 """Construct ABINIT-calculator object. 

123 

124 Examples 

125 ======== 

126 Use default values: 

127 

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

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

130 >>> e = h.get_potential_energy() 

131 

132 """ 

133 

134 super().__init__( 

135 template=AbinitTemplate(), 

136 profile=profile, 

137 directory=directory, 

138 parameters=kwargs, 

139 )