Coverage for ase / calculators / elk.py: 88.89%
54 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-30 08:22 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-30 08:22 +0000
1"""
2`Elk <https://elk.sourceforge.io>`_ is an all-electron full-potential linearised
3augmented-plane wave (LAPW) code.
5.. versionchanged:: 3.26.0
6 :class:`ELK` is now a subclass of :class:`GenericFileIOCalculator`.
8.. |config| replace:: ``config.ini``
9.. _config: calculators.html#calculator-configuration
11:class:`ELK` can be configured with |config|_.
13.. code-block:: ini
15 [elk]
16 command = /path/to/elk
17 sppath = /path/to/species
19If you need to override it for programmatic control of the ``elk`` command,
20use :class:`ElkProfile`.
22.. code-block:: python
24 from ase.calculators.elk import ELK, ElkProfile
26 profile = ElkProfile(command='/path/to/elk')
27 calc = ELK(profile=profile)
29"""
31import os
32import re
33import warnings
34from pathlib import Path
36from ase.calculators.genericfileio import (
37 BaseProfile,
38 CalculatorTemplate,
39 GenericFileIOCalculator,
40 read_stdout,
41)
42from ase.io.elk import ElkReader, write_elk_in
44COMPATIBILITY_MSG = (
45 '`ELK` has been restructured. '
46 'Please use `ELK(profile=ElkProfile(command))` instead.'
47)
50class ElkProfile(BaseProfile):
51 """Profile for :class:`ELK`."""
53 configvars = {'sppath'}
55 def __init__(self, command, sppath: str | None = None, **kwargs) -> None:
56 super().__init__(command, **kwargs)
57 self.sppath = sppath
59 def get_calculator_command(self, inputfile):
60 return []
62 def version(self):
63 output = read_stdout(self._split_command)
64 match = re.search(r'Elk code version (\S+)', output, re.M)
65 return match.group(1)
68class ElkTemplate(CalculatorTemplate):
69 """Template for :class:`ELK`."""
71 def __init__(self):
72 super().__init__('elk', ['energy', 'forces'])
73 self.inputname = 'elk.in'
74 self.outputname = 'elk.out'
76 def write_input(
77 self,
78 profile: ElkProfile,
79 directory,
80 atoms,
81 parameters,
82 properties,
83 ):
84 directory = Path(directory)
85 parameters = dict(parameters)
86 if 'forces' in properties:
87 parameters['tforce'] = True
88 if 'sppath' not in parameters and profile.sppath:
89 parameters['sppath'] = profile.sppath
90 write_elk_in(directory / self.inputname, atoms, parameters=parameters)
92 def execute(self, directory, profile: ElkProfile) -> None:
93 profile.run(directory, self.inputname, self.outputname)
95 def read_results(self, directory):
96 from ase.outputs import Properties
98 reader = ElkReader(directory)
99 dct = dict(reader.read_everything())
101 converged = dct.pop('converged')
102 if not converged:
103 raise RuntimeError('Did not converge')
105 # (Filter results thorugh Properties for error detection)
106 props = Properties(dct)
107 return dict(props)
109 def load_profile(self, cfg, **kwargs):
110 return ElkProfile.from_config(cfg, self.name, **kwargs)
113class ELK(GenericFileIOCalculator):
114 """Elk calculator."""
116 def __init__(
117 self,
118 *,
119 profile=None,
120 command=GenericFileIOCalculator._deprecated,
121 label=GenericFileIOCalculator._deprecated,
122 directory='.',
123 **kwargs,
124 ) -> None:
125 """
127 Parameters
128 ----------
129 **kwargs : dict, optional
130 ASE standard keywords like ``xc``, ``kpts`` and ``smearing`` (in ASE
131 units) or any Elk-native keywords (numeric parameters in Elk units).
132 All numeric parameters that are passed using Elk-native keys must be
133 in Elk units.
135 Examples
136 --------
137 >>> import numpy as np
138 >>> from ase.calculators.elk import ELK
139 >>> calc = ELK(tasks=[0], ngridk=np.array([3, 3, 3]))
140 >>> params = {'tasks': [[0], [10]], 'ngridk': [8, 8, 8], 'nempty': 8,
141 'bfieldc': np.array((0.0, 0.0, -0.01)), 'spinpol': True,
142 'dft+u': ((2, 1), (1, 2, 0.183, 0.034911967))}
143 >>> calc = ELK(**params)
144 Note: ``np.array((0.0, 0.0, -0.01))``, ``[0.0, 0.0, -0.01]``,
145 ``[[0.0, 0.0, -0.01]]``, and any combinations of lists, tuples,
146 and ndarrays, are equivalent
147 """
148 if command is not self._deprecated:
149 raise RuntimeError(COMPATIBILITY_MSG)
151 if label is not self._deprecated:
152 msg = 'Ignoring label, please use directory instead'
153 warnings.warn(msg, FutureWarning)
155 if 'ASE_ELK_COMMAND' in os.environ and profile is None:
156 warnings.warn(COMPATIBILITY_MSG, FutureWarning)
158 super().__init__(
159 template=ElkTemplate(),
160 profile=profile,
161 directory=directory,
162 parameters=kwargs,
163 )