Coverage for /builds/alexhroom/ase/ase/calculators/espresso.py: 96.77%
62 statements
« prev ^ index » next coverage.py v7.5.3, created at 2024-08-05 14:37 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2024-08-05 14:37 +0000
1"""Quantum ESPRESSO Calculator
3Run pw.x jobs.
4"""
7import os
8import warnings
9from pathlib import Path
11from ase.calculators.genericfileio import (BaseProfile, CalculatorTemplate,
12 GenericFileIOCalculator,
13 read_stdout)
14from ase.io import read, write
15from ase.io.espresso import Namelist
17compatibility_msg = (
18 'Espresso calculator is being restructured. Please use e.g. '
19 "Espresso(profile=EspressoProfile(argv=['mpiexec', 'pw.x'])) "
20 'to customize command-line arguments.'
21)
24# XXX We should find a way to display this warning.
25# warn_template = 'Property "%s" is None. Typically, this is because the ' \
26# 'required information has not been printed by Quantum ' \
27# 'Espresso at a "low" verbosity level (the default). ' \
28# 'Please try running Quantum Espresso with "high" verbosity.'
31class EspressoProfile(BaseProfile):
32 configvars = {'pseudo_dir'}
34 def __init__(self, command, pseudo_dir, **kwargs):
35 super().__init__(command, **kwargs)
36 self.pseudo_dir = Path(pseudo_dir)
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 stdout = read_stdout(self._split_command)
48 return self.parse_version(stdout)
50 def get_calculator_command(self, inputfile):
51 return ['-in', inputfile]
54class EspressoTemplate(CalculatorTemplate):
55 _label = 'espresso'
57 def __init__(self):
58 super().__init__(
59 'espresso',
60 ['energy', 'free_energy', 'forces', 'stress', 'magmoms', 'dipole'],
61 )
62 self.inputname = f'{self._label}.pwi'
63 self.outputname = f'{self._label}.pwo'
64 self.errorname = f"{self._label}.err"
66 def write_input(self, profile, directory, atoms, parameters, properties):
67 dst = directory / self.inputname
69 input_data = Namelist(parameters.pop("input_data", None))
70 input_data.to_nested("pw")
71 input_data["control"].setdefault("pseudo_dir", str(profile.pseudo_dir))
73 parameters["input_data"] = input_data
75 write(
76 dst,
77 atoms,
78 format='espresso-in',
79 properties=properties,
80 **parameters,
81 )
83 def execute(self, directory, profile):
84 profile.run(directory, self.inputname, self.outputname,
85 errorfile=self.errorname)
87 def read_results(self, directory):
88 path = directory / self.outputname
89 atoms = read(path, format='espresso-out')
90 return dict(atoms.calc.properties())
92 def load_profile(self, cfg, **kwargs):
93 return EspressoProfile.from_config(cfg, self.name, **kwargs)
95 def socketio_parameters(self, unixsocket, port):
96 return {}
98 def socketio_argv(self, profile, unixsocket, port):
99 if unixsocket:
100 ipi_arg = f'{unixsocket}:UNIX'
101 else:
102 ipi_arg = f'localhost:{port:d}' # XXX should take host, too
103 return profile.get_calculator_command(self.inputname) + [
104 '--ipi',
105 ipi_arg,
106 ]
109class Espresso(GenericFileIOCalculator):
110 def __init__(
111 self,
112 *,
113 profile=None,
114 command=GenericFileIOCalculator._deprecated,
115 label=GenericFileIOCalculator._deprecated,
116 directory='.',
117 **kwargs,
118 ):
119 """
120 All options for pw.x are copied verbatim to the input file, and put
121 into the correct section. Use ``input_data`` for parameters that are
122 already in a dict.
124 input_data: dict
125 A flat or nested dictionary with input parameters for pw.x
126 pseudopotentials: dict
127 A filename for each atomic species, e.g.
128 ``{'O': 'O.pbe-rrkjus.UPF', 'H': 'H.pbe-rrkjus.UPF'}``.
129 A dummy name will be used if none are given.
130 kspacing: float
131 Generate a grid of k-points with this as the minimum distance,
132 in A^-1 between them in reciprocal space. If set to None, kpts
133 will be used instead.
134 kpts: (int, int, int), dict, or BandPath
135 If kpts is a tuple (or list) of 3 integers, it is interpreted
136 as the dimensions of a Monkhorst-Pack grid.
137 If ``kpts`` is set to ``None``, only the Γ-point will be included
138 and QE will use routines optimized for Γ-point-only calculations.
139 Compared to Γ-point-only calculations without this optimization
140 (i.e. with ``kpts=(1, 1, 1)``), the memory and CPU requirements
141 are typically reduced by half.
142 If kpts is a dict, it will either be interpreted as a path
143 in the Brillouin zone (*) if it contains the 'path' keyword,
144 otherwise it is converted to a Monkhorst-Pack grid (**).
145 (*) see ase.dft.kpoints.bandpath
146 (**) see ase.calculators.calculator.kpts2sizeandoffsets
147 koffset: (int, int, int)
148 Offset of kpoints in each direction. Must be 0 (no offset) or
149 1 (half grid offset). Setting to True is equivalent to (1, 1, 1).
151 """
153 if command is not self._deprecated:
154 raise RuntimeError(compatibility_msg)
156 if label is not self._deprecated:
157 warnings.warn(
158 'Ignoring label, please use directory instead', FutureWarning
159 )
161 if 'ASE_ESPRESSO_COMMAND' in os.environ and profile is None:
162 warnings.warn(compatibility_msg, FutureWarning)
164 template = EspressoTemplate()
165 super().__init__(
166 profile=profile,
167 template=template,
168 directory=directory,
169 parameters=kwargs,
170 )