Coverage for /builds/debichem-team/python-ase/ase/calculators/abinit.py: 94.23%
52 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-03-06 04:00 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-03-06 04:00 +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 (
11 BaseProfile,
12 CalculatorTemplate,
13 GenericFileIOCalculator,
14)
17class AbinitProfile(BaseProfile):
18 configvars = {'pp_paths'}
20 def __init__(self, command, *, pp_paths=None, **kwargs):
21 super().__init__(command, **kwargs)
22 # XXX pp_paths is a raw configstring when it gets here.
23 # All the config stuff should have been loaded somehow by now,
24 # so this should be refactored.
25 if isinstance(pp_paths, str):
26 pp_paths = [path for path in pp_paths.splitlines() if path]
27 if pp_paths is None:
28 pp_paths = []
29 self.pp_paths = pp_paths
31 def version(self):
32 argv = [*self._split_command, '--version']
33 return check_output(argv, encoding='ascii').strip()
35 def get_calculator_command(self, inputfile):
36 return [str(inputfile)]
38 def socketio_argv_unix(self, socket):
39 # XXX clean up the passing of the inputfile
40 inputfile = AbinitTemplate().input_file
41 return [inputfile, '--ipi', f'{socket}:UNIX']
44class AbinitTemplate(CalculatorTemplate):
45 _label = 'abinit' # Controls naming of files within calculation directory
47 def __init__(self):
48 super().__init__(
49 name='abinit',
50 implemented_properties=[
51 'energy',
52 'free_energy',
53 'forces',
54 'stress',
55 'magmom',
56 ],
57 )
59 # XXX superclass should require inputname and outputname
61 self.inputname = f'{self._label}.in'
62 self.outputname = f'{self._label}.log'
63 self.errorname = f'{self._label}.err'
65 def execute(self, directory, profile) -> None:
66 profile.run(directory, self.inputname, self.outputname,
67 errorfile=self.errorname)
69 def write_input(self, profile, directory, atoms, parameters, properties):
70 directory = Path(directory)
71 parameters = dict(parameters)
72 pp_paths = parameters.pop('pp_paths', profile.pp_paths)
73 assert pp_paths is not None
75 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi')
76 kw.update(parameters)
78 io.prepare_abinit_input(
79 directory=directory,
80 atoms=atoms,
81 properties=properties,
82 parameters=kw,
83 pp_paths=pp_paths,
84 )
86 def read_results(self, directory):
87 return io.read_abinit_outputs(directory, self._label)
89 def load_profile(self, cfg, **kwargs):
90 return AbinitProfile.from_config(cfg, self.name, **kwargs)
92 def socketio_argv(self, profile, unixsocket, port):
93 # XXX This handling of --ipi argument is used by at least two
94 # calculators, should refactor if needed yet again
95 if unixsocket:
96 ipi_arg = f'{unixsocket}:UNIX'
97 else:
98 ipi_arg = f'localhost:{port:d}'
100 return profile.get_calculator_command(self.inputname) + [
101 '--ipi',
102 ipi_arg,
103 ]
105 def socketio_parameters(self, unixsocket, port):
106 return dict(ionmov=28, expert_user=1, optcell=2)
109class Abinit(GenericFileIOCalculator):
110 """Class for doing ABINIT calculations.
112 The default parameters are very close to those that the ABINIT
113 Fortran code would use. These are the exceptions::
115 calc = Abinit(xc='LDA', ecut=400, toldfe=1e-5)
116 """
118 def __init__(
119 self,
120 *,
121 profile=None,
122 directory='.',
123 **kwargs,
124 ):
125 """Construct ABINIT-calculator object.
127 Examples
128 ========
129 Use default values:
131 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001))
132 >>> h.center(vacuum=3.0)
133 >>> e = h.get_potential_energy()
135 """
137 super().__init__(
138 template=AbinitTemplate(),
139 profile=profile,
140 directory=directory,
141 parameters=kwargs,
142 )