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