Coverage for /builds/kinetik161/ase/ase/calculators/espresso.py: 90.00%
60 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"""Quantum ESPRESSO Calculator
3Run pw.x jobs.
4"""
7import os
8import warnings
10from ase.calculators.genericfileio import (
11 CalculatorTemplate,
12 GenericFileIOCalculator,
13 read_stdout,
14 BaseProfile,
15)
16from ase.io import read, write
18compatibility_msg = (
19 'Espresso calculator is being restructured. Please use e.g. '
20 "Espresso(profile=EspressoProfile(argv=['mpiexec', 'pw.x'])) "
21 'to customize command-line arguments.'
22)
25# XXX We should find a way to display this warning.
26# warn_template = 'Property "%s" is None. Typically, this is because the ' \
27# 'required information has not been printed by Quantum ' \
28# 'Espresso at a "low" verbosity level (the default). ' \
29# 'Please try running Quantum Espresso with "high" verbosity.'
32class EspressoProfile(BaseProfile):
33 def __init__(self, binary, pseudo_path, **kwargs):
34 super().__init__(**kwargs)
35 self.binary = binary
36 self.pseudo_path = pseudo_path
38 @staticmethod
39 def parse_version(stdout):
40 import re
42 match = re.match(r'\s*Program PWSCF\s*v\.(\S+)', stdout, re.M)
43 assert match is not None
44 return match.group(1)
46 def version(self):
47 try:
48 stdout = read_stdout(self.binary)
49 return self.parse_version(stdout)
50 except FileNotFoundError:
51 warnings.warn(
52 f'The executable {self.binary} is not found on the path'
53 )
54 return None
56 def get_calculator_command(self, inputfile):
57 return [self.binary, '-in', inputfile]
60class EspressoTemplate(CalculatorTemplate):
61 def __init__(self):
62 super().__init__(
63 'espresso',
64 ['energy', 'free_energy', 'forces', 'stress', 'magmoms', 'dipole'],
65 )
66 self.inputname = 'espresso.pwi'
67 self.outputname = 'espresso.pwo'
69 def write_input(self, profile, directory, atoms, parameters, properties):
70 dst = directory / self.inputname
71 write(
72 dst,
73 atoms,
74 format='espresso-in',
75 properties=properties,
76 pseudo_dir=str(profile.pseudo_path),
77 **parameters,
78 )
80 def execute(self, directory, profile):
81 profile.run(directory, self.inputname, directory / self.outputname)
83 def read_results(self, directory):
84 path = directory / self.outputname
85 atoms = read(path, format='espresso-out')
86 return dict(atoms.calc.properties())
88 def load_profile(self, cfg, **kwargs):
89 return EspressoProfile.from_config(cfg, self.name, **kwargs)
91 def socketio_parameters(self, unixsocket, port):
92 return {}
94 def socketio_argv(self, profile, unixsocket, port):
95 if unixsocket:
96 ipi_arg = f'{unixsocket}:UNIX'
97 else:
98 ipi_arg = f'localhost:{port:d}' # XXX should take host, too
99 return profile.get_calculator_command(self.inputname) + [
100 '--ipi',
101 ipi_arg,
102 ]
105class Espresso(GenericFileIOCalculator):
106 def __init__(
107 self,
108 *,
109 profile=None,
110 command=GenericFileIOCalculator._deprecated,
111 label=GenericFileIOCalculator._deprecated,
112 directory='.',
113 parallel_info=None,
114 parallel=True,
115 **kwargs,
116 ):
117 """
118 All options for pw.x are copied verbatim to the input file, and put
119 into the correct section. Use ``input_data`` for parameters that are
120 already in a dict, all other ``kwargs`` are passed as parameters.
122 Accepts all the options for pw.x as given in the QE docs, plus some
123 additional options:
125 input_data: dict
126 A flat or nested dictionary with input parameters for pw.x
127 pseudopotentials: dict
128 A filename for each atomic species, e.g.
129 ``{'O': 'O.pbe-rrkjus.UPF', 'H': 'H.pbe-rrkjus.UPF'}``.
130 A dummy name will be used if none are given.
131 kspacing: float
132 Generate a grid of k-points with this as the minimum distance,
133 in A^-1 between them in reciprocal space. If set to None, kpts
134 will be used instead.
135 kpts: (int, int, int), dict, or BandPath
136 If kpts is a tuple (or list) of 3 integers, it is interpreted
137 as the dimensions of a Monkhorst-Pack grid.
138 If ``kpts`` is set to ``None``, only the Γ-point will be included
139 and QE will use routines optimized for Γ-point-only calculations.
140 Compared to Γ-point-only calculations without this optimization
141 (i.e. with ``kpts=(1, 1, 1)``), the memory and CPU requirements
142 are typically reduced by half.
143 If kpts is a dict, it will either be interpreted as a path
144 in the Brillouin zone (*) if it contains the 'path' keyword,
145 otherwise it is converted to a Monkhorst-Pack grid (**).
146 (*) see ase.dft.kpoints.bandpath
147 (**) see ase.calculators.calculator.kpts2sizeandoffsets
148 koffset: (int, int, int)
149 Offset of kpoints in each direction. Must be 0 (no offset) or
150 1 (half grid offset). Setting to True is equivalent to (1, 1, 1).
153 .. note::
154 Set ``tprnfor=True`` and ``tstress=True`` to calculate forces and
155 stresses.
157 .. note::
158 Band structure plots can be made as follows:
161 1. Perform a regular self-consistent calculation,
162 saving the wave functions at the end, as well as
163 getting the Fermi energy:
165 >>> input_data = {<your input data>}
166 >>> calc = Espresso(input_data=input_data, ...)
167 >>> atoms.calc = calc
168 >>> atoms.get_potential_energy()
169 >>> fermi_level = calc.get_fermi_level()
171 2. Perform a non-self-consistent 'band structure' run
172 after updating your input_data and kpts keywords:
174 >>> input_data['control'].update({'calculation':'bands',
175 >>> 'restart_mode':'restart',
176 >>> 'verbosity':'high'})
177 >>> calc.set(kpts={<your Brillouin zone path>},
178 >>> input_data=input_data)
179 >>> calc.calculate(atoms)
181 3. Make the plot using the BandStructure functionality,
182 after setting the Fermi level to that of the prior
183 self-consistent calculation:
185 >>> bs = calc.band_structure()
186 >>> bs.reference = fermi_energy
187 >>> bs.plot()
189 """
191 if command is not self._deprecated:
192 raise RuntimeError(compatibility_msg)
194 if label is not self._deprecated:
195 import warnings
197 warnings.warn(
198 'Ignoring label, please use directory instead', FutureWarning
199 )
201 if 'ASE_ESPRESSO_COMMAND' in os.environ and profile is None:
202 import warnings
204 warnings.warn(compatibility_msg, FutureWarning)
206 template = EspressoTemplate()
207 super().__init__(
208 profile=profile,
209 template=template,
210 directory=directory,
211 parallel_info=parallel_info,
212 parallel=parallel,
213 parameters=kwargs,
214 )