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
« 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.
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 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
27 def version(self):
28 argv = [self.binary, '--version']
29 return check_output(argv, encoding='ascii').strip()
31 def get_calculator_command(self, inputfile):
32 return [self.binary, str(inputfile)]
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']
40class AbinitTemplate(CalculatorTemplate):
41 _label = 'abinit' # Controls naming of files within calculation directory
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 )
55 # XXX superclass should require inputname and outputname
57 self.inputname = f'{self._label}.in'
58 self.outputname = f'{self._label}.log'
59 self.errorname = f'{self._label}.err'
61 def execute(self, directory, profile) -> None:
62 profile.run(directory, self.inputname, self.outputname,
63 errorfile=self.errorname)
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
71 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi')
72 kw.update(parameters)
74 io.prepare_abinit_input(
75 directory=directory,
76 atoms=atoms,
77 properties=properties,
78 parameters=kw,
79 pp_paths=pp_paths,
80 )
82 def read_results(self, directory):
83 return io.read_abinit_outputs(directory, self._label)
85 def load_profile(self, cfg, **kwargs):
86 return AbinitProfile.from_config(cfg, self.name, **kwargs)
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}'
96 return profile.get_calculator_command(self.inputname) + [
97 '--ipi',
98 ipi_arg,
99 ]
101 def socketio_parameters(self, unixsocket, port):
102 return dict(ionmov=28, expert_user=1, optcell=2)
105class Abinit(GenericFileIOCalculator):
106 """Class for doing ABINIT calculations.
108 The default parameters are very close to those that the ABINIT
109 Fortran code would use. These are the exceptions::
111 calc = Abinit(label='abinit', xc='LDA', ecut=400, toldfe=1e-5)
112 """
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.
125 Parameters
126 ==========
127 label: str
128 Prefix to use for filenames (label.in, label.txt, ...).
129 Default is 'abinit'.
131 Examples
132 ========
133 Use default values:
135 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001))
136 >>> h.center(vacuum=3.0)
137 >>> e = h.get_potential_energy()
139 """
141 super().__init__(
142 template=AbinitTemplate(),
143 profile=profile,
144 directory=directory,
145 parallel_info=parallel_info,
146 parallel=parallel,
147 parameters=kwargs,
148 )