• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit-nature / 5055120412

pending completion
5055120412

push

github

GitHub
Improve docs (backport #1175) (#1176)

18584 of 21641 relevant lines covered (85.87%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.9
/qiskit_nature/second_q/drivers/psi4d/psi4driver.py
1
# This code is part of Qiskit.
2
#
3
# (C) Copyright IBM 2018, 2022.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
""" Psi4 Driver """
1✔
14

15
from __future__ import annotations
1✔
16

17
import logging
1✔
18
import os
1✔
19
import subprocess
1✔
20
import tempfile
1✔
21
from pathlib import Path
1✔
22
from typing import Any
1✔
23

24
from qiskit_nature import QiskitNatureError
1✔
25
from qiskit_nature.exceptions import UnsupportMethodError
1✔
26
from qiskit_nature.units import DistanceUnit
1✔
27
from qiskit_nature.second_q.problems import ElectronicBasis, ElectronicStructureProblem
1✔
28
from qiskit_nature.second_q.formats.molecule_info import MoleculeInfo
1✔
29
from qiskit_nature.second_q.formats.qcschema import QCSchema
1✔
30
from qiskit_nature.second_q.formats.qcschema_translator import qcschema_to_problem
1✔
31
import qiskit_nature.optionals as _optionals
1✔
32

33
from ..electronic_structure_driver import ElectronicStructureDriver, MethodType, _QCSchemaData
1✔
34

35
logger = logging.getLogger(__name__)
1✔
36

37

38
@_optionals.HAS_PSI4.require_in_instance
1✔
39
class Psi4Driver(ElectronicStructureDriver):
1✔
40
    """
41
    Qiskit Nature driver using the Psi4 program.
42
    See http://www.psicode.org/
43
    """
44

45
    def __init__(
1✔
46
        self,
47
        config: str
48
        | list[
49
            str
50
        ] = "molecule h2 {\n  0 1\n  H  0.0 0.0 0.0\n  H  0.0 0.0 0.735\n  no_com\n  no_reorient\n}\n\n"
51
        "set {\n  basis sto-3g\n  scf_type pk\n  reference rhf\n",
52
    ) -> None:
53
        """
54
        Args:
55
            config: A molecular configuration conforming to Psi4 format.
56
        Raises:
57
            QiskitNatureError: Psi4 Driver configuration should be a string or list of strings.
58
        """
59
        super().__init__()
1✔
60
        if not isinstance(config, str) and not isinstance(config, list):
1✔
61
            raise QiskitNatureError(
1✔
62
                f"Psi4 Driver configuration should be a string or list of strings:'{config}'."
63
            )
64

65
        if isinstance(config, list):
1✔
66
            config = "\n".join(config)
1✔
67

68
        self._config = config
1✔
69
        self._qcschemadata = _QCSchemaData()
1✔
70

71
    @staticmethod
1✔
72
    @_optionals.HAS_PSI4.require_in_call
1✔
73
    def from_molecule(
1✔
74
        molecule: MoleculeInfo,
75
        basis: str = "sto3g",
76
        method: MethodType = MethodType.RHF,
77
        driver_kwargs: dict[str, Any] | None = None,
78
    ) -> "Psi4Driver":
79
        """Creates a driver from a molecule.
80

81
        Args:
82
            molecule: the molecular information.
83
            basis: the basis set.
84
            method: the SCF method type.
85
            driver_kwargs: keyword arguments to be passed to driver.
86

87
        Returns:
88
            The constructed driver instance.
89

90
        Raises:
91
            QiskitNatureError: when an unknown unit is encountered.
92
        """
93
        # Ignore kwargs parameter for this driver
94
        del driver_kwargs
1✔
95
        Psi4Driver.check_method_supported(method)
1✔
96
        basis = Psi4Driver.to_driver_basis(basis)
1✔
97

98
        if molecule.units == DistanceUnit.ANGSTROM:
1✔
99
            units = "ang"
1✔
100
        elif molecule.units == DistanceUnit.BOHR:
×
101
            units = "bohr"
×
102
        else:
103
            raise QiskitNatureError(f"Unknown unit '{molecule.units.value}'")
×
104

105
        name = "".join(molecule.symbols)
1✔
106
        geom = "\n".join(
1✔
107
            [
108
                name + " " + " ".join(map(str, coord))
109
                for name, coord in zip(molecule.symbols, molecule.coords)
110
            ]
111
        )
112
        cfg1 = f"molecule {name} {{\nunits {units}\n"
1✔
113
        cfg2 = f"{molecule.charge} {molecule.multiplicity}\n"
1✔
114
        cfg3 = f"{geom}\nno_com\nno_reorient\n}}\n\n"
1✔
115
        cfg4 = f"set {{\n basis {basis}\n scf_type pk\n reference {method.value}\n}}"
1✔
116
        return Psi4Driver(cfg1 + cfg2 + cfg3 + cfg4)
1✔
117

118
    @staticmethod
1✔
119
    def to_driver_basis(basis: str) -> str:
120
        """Converts basis to a driver acceptable basis.
121

122
        Args:
123
            basis: The basis set to be used.
124

125
        Returns:
126
            A driver acceptable basis.
127
        """
128
        if basis == "sto3g":
1✔
129
            return "sto-3g"
1✔
130
        return basis
×
131

132
    @staticmethod
1✔
133
    def check_method_supported(method: MethodType) -> None:
134
        """Checks that Psi4 supports this method.
135

136
        Args:
137
            method: the SCF method type.
138

139
        Raises:
140
            UnsupportMethodError: If the method is not supported.
141
        """
142
        if method not in [MethodType.RHF, MethodType.ROHF, MethodType.UHF]:
1✔
143
            raise UnsupportMethodError(f"Invalid Psi4 method {method.value}.")
×
144

145
    def run(self) -> ElectronicStructureProblem:
1✔
146
        cfg = self._config
1✔
147

148
        psi4d_directory = Path(__file__).resolve().parent
1✔
149
        template_file = psi4d_directory.joinpath("_template.txt")
1✔
150

151
        input_text = [cfg]
1✔
152

153
        file_fd, hdf5_file = tempfile.mkstemp(suffix=".hdf5")
1✔
154
        os.close(file_fd)
1✔
155
        input_text += [f'_FILE_PATH = "{Path(hdf5_file).as_posix()}"']
1✔
156

157
        with open(template_file, "r", encoding="utf8") as file:
1✔
158
            input_text += [line.strip("\n") for line in file.readlines()]
1✔
159

160
        file_fd, input_file = tempfile.mkstemp(suffix=".inp")
1✔
161
        os.close(file_fd)
1✔
162
        with open(input_file, "w", encoding="utf8") as stream:
1✔
163
            stream.write("\n".join(input_text))
1✔
164

165
        file_fd, output_file = tempfile.mkstemp(suffix=".out")
1✔
166
        os.close(file_fd)
1✔
167
        try:
1✔
168
            Psi4Driver._run_psi4(input_file, output_file)
1✔
169
            if logger.isEnabledFor(logging.DEBUG):
1✔
170
                with open(output_file, "r", encoding="utf8") as file:
×
171
                    logger.debug("Psi4 output file:\n%s", file.read())
×
172
        finally:
173
            run_directory = os.getcwd()
1✔
174
            for local_file in os.listdir(run_directory):
1✔
175
                if local_file.endswith(".clean"):
1✔
176
                    os.remove(run_directory + "/" + local_file)
1✔
177
            try:
1✔
178
                os.remove("timer.dat")
1✔
179
            except Exception:  # pylint: disable=broad-except
×
180
                pass
×
181

182
            try:
1✔
183
                os.remove(input_file)
1✔
184
            except Exception:  # pylint: disable=broad-except
×
185
                pass
×
186

187
            try:
1✔
188
                os.remove(output_file)
1✔
189
            except Exception:  # pylint: disable=broad-except
×
190
                pass
×
191

192
        self._qcschemadata = _QCSchemaData.from_hdf5(hdf5_file)
1✔
193
        try:
1✔
194
            os.remove(hdf5_file)
1✔
195
        except Exception:  # pylint: disable=broad-except
×
196
            pass
×
197

198
        return self.to_problem()
1✔
199

200
    def to_qcschema(self, *, include_dipole: bool = True) -> QCSchema:
1✔
201
        """Extracts all available information after the driver was run into a :class:`.QCSchema`
202
        object.
203

204
        Args:
205
            include_dipole: whether to include the custom dipole integrals in the QCSchema.
206

207
        Returns:
208
            A :class:`.QCSchema` storing all extracted system data computed by the driver.
209
        """
210
        return Psi4Driver._to_qcschema(self._qcschemadata, include_dipole=include_dipole)
1✔
211

212
    def to_problem(
1✔
213
        self,
214
        *,
215
        basis: ElectronicBasis = ElectronicBasis.MO,
216
        include_dipole: bool = True,
217
    ) -> ElectronicStructureProblem:
218
        return qcschema_to_problem(
1✔
219
            self.to_qcschema(include_dipole=include_dipole),
220
            basis=basis,
221
            include_dipole=include_dipole,
222
        )
223

224
    @staticmethod
1✔
225
    def _run_psi4(input_file, output_file):
226
        process = None
1✔
227
        try:
1✔
228
            with subprocess.Popen(
1✔
229
                [_optionals.PSI4, input_file, output_file],
230
                stdout=subprocess.PIPE,
231
                universal_newlines=True,
232
            ) as process:
233
                stdout, _ = process.communicate()
1✔
234
                process.wait()
1✔
235
        except Exception as ex:
×
236
            if process is not None:
×
237
                process.kill()
×
238

239
            raise QiskitNatureError(f"{_optionals.PSI4} run has failed") from ex
×
240

241
        if process.returncode != 0:
1✔
242
            errmsg = ""
1✔
243
            if stdout is not None:
1✔
244
                lines = stdout.splitlines()
1✔
245
                for line in lines:
1✔
246
                    logger.error(line)
1✔
247
                    errmsg += line + "\n"
1✔
248
            raise QiskitNatureError(
1✔
249
                f"{_optionals.PSI4} process return code {process.returncode}\n{errmsg}"
250
            )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc