Coverage for /builds/ase/ase/ase/calculators/abinit.py: 94.23%
52 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3"""This module defines an ASE interface to ABINIT.
5http://www.abinit.org/
6"""
8from pathlib import Path
9from subprocess import check_output
11import ase.io.abinit as io
12from ase.calculators.genericfileio import (
13 BaseProfile,
14 CalculatorTemplate,
15 GenericFileIOCalculator,
16)
19class AbinitProfile(BaseProfile):
20 configvars = {'pp_paths'}
22 def __init__(self, command, *, pp_paths=None, **kwargs):
23 super().__init__(command, **kwargs)
24 # XXX pp_paths is a raw configstring when it gets here.
25 # All the config stuff should have been loaded somehow by now,
26 # so this should be refactored.
27 if isinstance(pp_paths, str):
28 pp_paths = [path for path in pp_paths.splitlines() if path]
29 if pp_paths is None:
30 pp_paths = []
31 self.pp_paths = pp_paths
33 def version(self):
34 argv = [*self._split_command, '--version']
35 return check_output(argv, encoding='ascii').strip()
37 def get_calculator_command(self, inputfile):
38 return [str(inputfile)]
40 def socketio_argv_unix(self, socket):
41 # XXX clean up the passing of the inputfile
42 inputfile = AbinitTemplate().input_file
43 return [inputfile, '--ipi', f'{socket}:UNIX']
46class AbinitTemplate(CalculatorTemplate):
47 _label = 'abinit' # Controls naming of files within calculation directory
49 def __init__(self):
50 super().__init__(
51 name='abinit',
52 implemented_properties=[
53 'energy',
54 'free_energy',
55 'forces',
56 'stress',
57 'magmom',
58 ],
59 )
61 # XXX superclass should require inputname and outputname
63 self.inputname = f'{self._label}.in'
64 self.outputname = f'{self._label}.log'
65 self.errorname = f'{self._label}.err'
67 def execute(self, directory, profile) -> None:
68 profile.run(directory, self.inputname, self.outputname,
69 errorfile=self.errorname)
71 def write_input(self, profile, directory, atoms, parameters, properties):
72 directory = Path(directory)
73 parameters = dict(parameters)
74 pp_paths = parameters.pop('pp_paths', profile.pp_paths)
75 assert pp_paths is not None
77 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi')
78 kw.update(parameters)
80 io.prepare_abinit_input(
81 directory=directory,
82 atoms=atoms,
83 properties=properties,
84 parameters=kw,
85 pp_paths=pp_paths,
86 )
88 def read_results(self, directory):
89 return io.read_abinit_outputs(directory, self._label)
91 def load_profile(self, cfg, **kwargs):
92 return AbinitProfile.from_config(cfg, self.name, **kwargs)
94 def socketio_argv(self, profile, unixsocket, port):
95 # XXX This handling of --ipi argument is used by at least two
96 # calculators, should refactor if needed yet again
97 if unixsocket:
98 ipi_arg = f'{unixsocket}:UNIX'
99 else:
100 ipi_arg = f'localhost:{port:d}'
102 return profile.get_calculator_command(self.inputname) + [
103 '--ipi',
104 ipi_arg,
105 ]
107 def socketio_parameters(self, unixsocket, port):
108 return dict(ionmov=28, expert_user=1, optcell=2)
111class Abinit(GenericFileIOCalculator):
112 """Class for doing ABINIT calculations.
114 The default parameters are very close to those that the ABINIT
115 Fortran code would use. These are the exceptions::
117 calc = Abinit(xc='LDA', ecut=400, toldfe=1e-5)
118 """
120 def __init__(
121 self,
122 *,
123 profile=None,
124 directory='.',
125 **kwargs,
126 ):
127 """Construct ABINIT-calculator object.
129 Examples
130 ========
131 Use default values:
133 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001))
134 >>> h.center(vacuum=3.0)
135 >>> e = h.get_potential_energy()
137 """
139 super().__init__(
140 template=AbinitTemplate(),
141 profile=profile,
142 directory=directory,
143 parameters=kwargs,
144 )