Coverage for /builds/kinetik161/ase/ase/calculators/abinit.py: 90.38%
52 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1"""This module defines an ASE interface to ABINIT.
3http://www.abinit.org/
4"""
6import re
7from pathlib import Path
8from subprocess import check_output
10import ase.io.abinit as io
11from ase.calculators.genericfileio import (
12 CalculatorTemplate,
13 GenericFileIOCalculator,
14 BaseProfile,
15)
18def get_abinit_version(command):
19 txt = check_output([command, '--version']).decode('ascii')
20 # This allows trailing stuff like betas, rc and so
21 m = re.match(r'\s*(\d\.\d\.\d)', txt)
22 if m is None:
23 raise RuntimeError(
24 'Cannot recognize abinit version. ' 'Start of output: {}'.format(
25 txt[:40]
26 )
27 )
28 return m.group(1)
31class AbinitProfile(BaseProfile):
32 def __init__(self, binary, **kwargs):
33 super().__init__(**kwargs)
34 self.binary = binary
36 def version(self):
37 return check_output(
38 self.binary + ['--version'], encoding='ascii'
39 ).strip()
41 def get_calculator_command(self, inputfile):
42 return [self.binary, str(inputfile)]
44 def socketio_argv_unix(self, socket):
45 # XXX clean up the passing of the inputfile
46 inputfile = AbinitTemplate().input_file
47 return [self.binary, inputfile, '--ipi', f'{socket}:UNIX']
50class AbinitTemplate(CalculatorTemplate):
51 _label = 'abinit' # Controls naming of files within calculation directory
53 def __init__(self):
54 super().__init__(
55 name='abinit',
56 implemented_properties=[
57 'energy',
58 'free_energy',
59 'forces',
60 'stress',
61 'magmom',
62 ],
63 )
65 # XXX superclass should require inputname and outputname
67 self.inputname = f'{self._label}.in'
68 self.outputname = f'{self._label}.log'
70 def execute(self, directory, profile) -> None:
71 profile.run(directory, self.inputname, self.outputname)
73 def write_input(self, profile, directory, atoms, parameters, properties):
74 directory = Path(directory)
75 parameters = dict(parameters)
76 pp_paths = parameters.pop('pp_paths', None)
77 assert pp_paths is not None
79 kw = dict(xc='LDA', smearing=None, kpts=None, raw=None, pps='fhi')
80 kw.update(parameters)
82 io.prepare_abinit_input(
83 directory=directory,
84 atoms=atoms,
85 properties=properties,
86 parameters=kw,
87 pp_paths=pp_paths,
88 )
90 def read_results(self, directory):
91 return io.read_abinit_outputs(directory, self._label)
93 def load_profile(self, cfg, **kwargs):
94 return AbinitProfile.from_config(cfg, self.name, **kwargs)
96 def socketio_argv(self, profile, unixsocket, port):
97 # XXX This handling of --ipi argument is used by at least two
98 # calculators, should refactor if needed yet again
99 if unixsocket:
100 ipi_arg = f'{unixsocket}:UNIX'
101 else:
102 ipi_arg = f'localhost:{port:d}'
104 return profile.get_calculator_command(self.inputname) + [
105 '--ipi',
106 ipi_arg,
107 ]
109 def socketio_parameters(self, unixsocket, port):
110 return dict(ionmov=28, expert_user=1, optcell=2)
113class Abinit(GenericFileIOCalculator):
114 """Class for doing ABINIT calculations.
116 The default parameters are very close to those that the ABINIT
117 Fortran code would use. These are the exceptions::
119 calc = Abinit(label='abinit', xc='LDA', ecut=400, toldfe=1e-5)
120 """
122 def __init__(
123 self,
124 *,
125 profile=None,
126 directory='.',
127 parallel_info=None,
128 parallel=True,
129 **kwargs,
130 ):
131 """Construct ABINIT-calculator object.
133 Parameters
134 ==========
135 label: str
136 Prefix to use for filenames (label.in, label.txt, ...).
137 Default is 'abinit'.
139 Examples
140 ========
141 Use default values:
143 >>> h = Atoms('H', calculator=Abinit(ecut=200, toldfe=0.001))
144 >>> h.center(vacuum=3.0)
145 >>> e = h.get_potential_energy()
147 """
149 super().__init__(
150 template=AbinitTemplate(),
151 profile=profile,
152 directory=directory,
153 parallel_info=parallel_info,
154 parallel=parallel,
155 parameters=kwargs,
156 )