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
« 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.
3http://www.abinit.org/
4"""
6from pathlib import Path
7from subprocess import check_output
9import ase.io.abinit as io
10from ase.calculators.genericfileio import (BaseProfile, CalculatorTemplate,
11 GenericFileIOCalculator)
14class AbinitProfile(BaseProfile):
15 configvars = {'pp_paths'}
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
28 def version(self):
29 argv = [*self._split_command, '--version']
30 return check_output(argv, encoding='ascii').strip()
32 def get_calculator_command(self, inputfile):
33 return [str(inputfile)]
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']
41class AbinitTemplate(CalculatorTemplate):
42 _label = 'abinit' # Controls naming of files within calculation directory
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 )
56 # XXX superclass should require inputname and outputname
58 self.inputname = f'{self._label}.in'
59 self.outputname = f'{self._label}.log'
60 self.errorname = f'{self._label}.err'
62 def execute(self, directory, profile) -> None:
63 profile.run(directory, self.inputname, self.outputname,
64 errorfile=self.errorname)
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
72 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi')
73 kw.update(parameters)
75 io.prepare_abinit_input(
76 directory=directory,
77 atoms=atoms,
78 properties=properties,
79 parameters=kw,
80 pp_paths=pp_paths,
81 )
83 def read_results(self, directory):
84 return io.read_abinit_outputs(directory, self._label)
86 def load_profile(self, cfg, **kwargs):
87 return AbinitProfile.from_config(cfg, self.name, **kwargs)
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}'
97 return profile.get_calculator_command(self.inputname) + [
98 '--ipi',
99 ipi_arg,
100 ]
102 def socketio_parameters(self, unixsocket, port):
103 return dict(ionmov=28, expert_user=1, optcell=2)
106class Abinit(GenericFileIOCalculator):
107 """Class for doing ABINIT calculations.
109 The default parameters are very close to those that the ABINIT
110 Fortran code would use. These are the exceptions::
112 calc = Abinit(xc='LDA', ecut=400, toldfe=1e-5)
113 """
115 def __init__(
116 self,
117 *,
118 profile=None,
119 directory='.',
120 **kwargs,
121 ):
122 """Construct ABINIT-calculator object.
124 Examples
125 ========
126 Use default values:
128 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001))
129 >>> h.center(vacuum=3.0)
130 >>> e = h.get_potential_energy()
132 """
134 super().__init__(
135 template=AbinitTemplate(),
136 profile=profile,
137 directory=directory,
138 parameters=kwargs,
139 )