def test_from_hdf5(self):
        """Test from_hdf5."""
        with tempfile.TemporaryFile() as tmp_file:
            with h5py.File(tmp_file, "w") as file:
                self.prop.to_hdf5(file)

            with h5py.File(tmp_file, "r") as file:
                read_prop = AngularMomentum.from_hdf5(file["AngularMomentum"])

                self.assertEqual(self.prop, read_prop)
class TestAngularMomentum(PropertyTest):
    """Test AngularMomentum Property"""

    def setUp(self):
        """Setup."""
        super().setUp()
        num_molecular_orbitals = 4
        self.prop = AngularMomentum(num_molecular_orbitals * 2)

    def test_second_q_ops(self):
        """Test second_q_ops."""
        op = self.prop.second_q_ops()["AngularMomentum"]
        with open(
            self.get_resource_path("angular_momentum_op.json", "second_q/properties/resources"),
            "r",
            encoding="utf8",
        ) as file:
            expected = json.load(file)
            expected_op = FermionicOp(expected).simplify()
        self.assertSetEqual(frozenset(op.to_list("dense")), frozenset(expected_op.to_list("dense")))

    def test_to_hdf5(self):
        """Test to_hdf5."""
        with tempfile.TemporaryFile() as tmp_file:
            with h5py.File(tmp_file, "w") as file:
                self.prop.to_hdf5(file)

    def test_from_hdf5(self):
        """Test from_hdf5."""
        with tempfile.TemporaryFile() as tmp_file:
            with h5py.File(tmp_file, "w") as file:
                self.prop.to_hdf5(file)

            with h5py.File(tmp_file, "r") as file:
                read_prop = AngularMomentum.from_hdf5(file["AngularMomentum"])

                self.assertEqual(self.prop, read_prop)
    def _construct_driver_result(self) -> ElectronicStructureDriverResult:
        driver_result = ElectronicStructureDriverResult()

        self._populate_driver_result_molecule(driver_result)
        self._populate_driver_result_metadata(driver_result)
        self._populate_driver_result_basis_transform(driver_result)
        self._populate_driver_result_particle_number(driver_result)
        self._populate_driver_result_electronic_energy(driver_result)

        # TODO: once https://github.com/Qiskit/qiskit-nature/issues/312 is fixed we can stop adding
        # these properties by default.
        # if not settings.dict_aux_operators:
        driver_result.add_property(AngularMomentum(self._nmo * 2))
        driver_result.add_property(Magnetization(self._nmo * 2))

        return driver_result
    def test_angular_momentum(self) -> None:
        """Tests the AngularMomentum property."""
        container = ElectronicPropertiesContainer()

        with self.subTest("initially None"):
            self.assertIsNone(container.angular_momentum)

        with self.subTest("wrong setting type"):
            with self.assertRaises(TypeError):
                container.angular_momentum = OccupiedModals()  # type: ignore[assignment]

        with self.subTest("successful setting"):
            container.angular_momentum = AngularMomentum(1)
            self.assertIn(AngularMomentum, container)

        with self.subTest("removal via None setting"):
            container.angular_momentum = None
            self.assertNotIn(AngularMomentum, container)
示例#5
0
    def run(self) -> ElectronicStructureDriverResult:
        cfg = self._config

        psi4d_directory = Path(__file__).resolve().parent
        template_file = psi4d_directory.joinpath("_template.txt")
        qiskit_nature_directory = psi4d_directory.parent.parent

        input_text = [cfg]
        input_text += ["import sys"]
        syspath = ("['" + qiskit_nature_directory.as_posix() + "','" +
                   "','".join(Path(p).as_posix() for p in sys.path) + "']")

        input_text += ["sys.path = " + syspath + " + sys.path"]

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

        file_fd, hdf5_file = tempfile.mkstemp(suffix=".hdf5")
        os.close(file_fd)

        input_text += [
            f'save_to_hdf5(_q_driver_result, "{Path(hdf5_file).as_posix()}", replace=True)'
        ]

        file_fd, input_file = tempfile.mkstemp(suffix=".inp")
        os.close(file_fd)
        with open(input_file, "w", encoding="utf8") as stream:
            stream.write("\n".join(input_text))

        file_fd, output_file = tempfile.mkstemp(suffix=".out")
        os.close(file_fd)
        try:
            PSI4Driver._run_psi4(input_file, output_file)
            if logger.isEnabledFor(logging.DEBUG):
                with open(output_file, "r", encoding="utf8") as file:
                    logger.debug("PSI4 output file:\n%s", file.read())
        finally:
            run_directory = os.getcwd()
            for local_file in os.listdir(run_directory):
                if local_file.endswith(".clean"):
                    os.remove(run_directory + "/" + local_file)
            try:
                os.remove("timer.dat")
            except Exception:  # pylint: disable=broad-except
                pass

            try:
                os.remove(input_file)
            except Exception:  # pylint: disable=broad-except
                pass

            try:
                os.remove(output_file)
            except Exception:  # pylint: disable=broad-except
                pass

        driver_result = cast(ElectronicStructureDriverResult,
                             load_from_hdf5(hdf5_file))

        try:
            os.remove(hdf5_file)
        except Exception:  # pylint: disable=broad-except
            pass

        # TODO: once https://github.com/Qiskit/qiskit-nature/issues/312 is fixed we can stop adding
        # these properties by default.
        # if not settings.dict_aux_operators:
        num_spin_orbitals = driver_result.get_property(
            "ParticleNumber").num_spin_orbitals
        driver_result.add_property(AngularMomentum(num_spin_orbitals))
        driver_result.add_property(Magnetization(num_spin_orbitals))

        # inject Psi4 config (because it is not available at runtime inside the template)
        driver_metadata = driver_result.get_property("DriverMetadata")
        driver_metadata.config = cfg

        return driver_result
    def _parse_matrix_file(fname: str, useao2e: bool = False) -> ElectronicStructureDriverResult:
        """
        get_driver_class is used here because the discovery routine will load all the gaussian
        binary dependencies, if not loaded already. It won't work without it.
        """
        try:
            # add gauopen to sys.path so that binaries can be loaded
            gauopen_directory = os.path.join(os.path.dirname(os.path.realpath(__file__)), "gauopen")
            if gauopen_directory not in sys.path:
                sys.path.insert(0, gauopen_directory)
            # pylint: disable=import-outside-toplevel
            from .gauopen.QCMatEl import MatEl
        except ImportError as mnfe:
            msg = (
                (
                    "qcmatrixio extension not found. "
                    "See Gaussian driver readme to build qcmatrixio.F using f2py"
                )
                if mnfe.name == "qcmatrixio"
                else str(mnfe)
            )

            logger.info(msg)
            raise QiskitNatureError(msg) from mnfe

        mel = MatEl(file=fname)
        logger.debug("MatrixElement file:\n%s", mel)

        driver_result = ElectronicStructureDriverResult()

        # molecule
        coords = np.reshape(mel.c, (len(mel.ian), 3))
        geometry: list[tuple[str, list[float]]] = []
        for atom, xyz in zip(mel.ian, coords):
            geometry.append((PERIODIC_TABLE[atom], BOHR * xyz))

        driver_result.molecule = Molecule(
            geometry,
            multiplicity=mel.multip,
            charge=mel.icharg,
        )

        # driver metadata
        driver_result.add_property(DriverMetadata("GAUSSIAN", mel.gversion, ""))

        # basis transform
        moc = GaussianDriver._get_matrix(mel, "ALPHA MO COEFFICIENTS")
        moc_b = GaussianDriver._get_matrix(mel, "BETA MO COEFFICIENTS")
        if np.array_equal(moc, moc_b):
            logger.debug("ALPHA and BETA MO COEFFS identical, keeping only ALPHA")
            moc_b = None

        nmo = moc.shape[0]

        basis_transform = ElectronicBasisTransform(
            ElectronicBasis.AO, ElectronicBasis.MO, moc, moc_b
        )
        driver_result.add_property(basis_transform)

        # particle number
        num_alpha = (mel.ne + mel.multip - 1) // 2
        num_beta = (mel.ne - mel.multip + 1) // 2

        driver_result.add_property(
            ParticleNumber(num_spin_orbitals=nmo * 2, num_particles=(num_alpha, num_beta))
        )

        # electronic energy
        hcore = GaussianDriver._get_matrix(mel, "CORE HAMILTONIAN ALPHA")
        logger.debug("CORE HAMILTONIAN ALPHA %s", hcore.shape)
        hcore_b = GaussianDriver._get_matrix(mel, "CORE HAMILTONIAN BETA")
        if np.array_equal(hcore, hcore_b):
            # From Gaussian interfacing documentation: "The two core Hamiltonians are identical
            # unless a Fermi contact perturbation has been applied."
            logger.debug("CORE HAMILTONIAN ALPHA and BETA identical, keeping only ALPHA")
            hcore_b = None
        logger.debug(
            "CORE HAMILTONIAN BETA %s",
            "- Not present" if hcore_b is None else hcore_b.shape,
        )
        one_body_ao = OneBodyElectronicIntegrals(ElectronicBasis.AO, (hcore, hcore_b))
        one_body_mo = one_body_ao.transform_basis(basis_transform)

        eri = GaussianDriver._get_matrix(mel, "REGULAR 2E INTEGRALS")
        logger.debug("REGULAR 2E INTEGRALS %s", eri.shape)
        if moc_b is None and mel.matlist.get("BB MO 2E INTEGRALS") is not None:
            # It seems that when using ROHF, where alpha and beta coeffs are
            # the same, that integrals
            # for BB and BA are included in the output, as well as just AA
            # that would have been expected
            # Using these fails to give the right answer (is ok for UHF).
            # So in this case we revert to
            # using 2 electron ints in atomic basis from the output and
            # converting them ourselves.
            useao2e = True
            logger.info(
                "Identical A and B coeffs but BB ints are present - using regular 2E ints instead"
            )
        two_body_ao = TwoBodyElectronicIntegrals(ElectronicBasis.AO, (eri, None, None, None))
        two_body_mo: TwoBodyElectronicIntegrals
        if useao2e:
            # eri are 2-body in AO. We can convert to MO via the ElectronicBasisTransform but using
            # ints in MO already, as in the else here, is better
            two_body_mo = two_body_ao.transform_basis(basis_transform)
        else:
            # These are in MO basis but by default will be reduced in size by frozen core default so
            # to use them we need to add Window=Full above when we augment the config
            mohijkl = GaussianDriver._get_matrix(mel, "AA MO 2E INTEGRALS")
            logger.debug("AA MO 2E INTEGRALS %s", mohijkl.shape)
            mohijkl_bb = GaussianDriver._get_matrix(mel, "BB MO 2E INTEGRALS")
            logger.debug(
                "BB MO 2E INTEGRALS %s",
                "- Not present" if mohijkl_bb is None else mohijkl_bb.shape,
            )
            mohijkl_ba = GaussianDriver._get_matrix(mel, "BA MO 2E INTEGRALS")
            logger.debug(
                "BA MO 2E INTEGRALS %s",
                "- Not present" if mohijkl_ba is None else mohijkl_ba.shape,
            )
            two_body_mo = TwoBodyElectronicIntegrals(
                ElectronicBasis.MO, (mohijkl, mohijkl_ba, mohijkl_bb, None)
            )

        electronic_energy = ElectronicEnergy(
            [one_body_ao, two_body_ao, one_body_mo, two_body_mo],
            nuclear_repulsion_energy=mel.scalar("ENUCREP"),
            reference_energy=mel.scalar("ETOTAL"),
        )

        kinetic = GaussianDriver._get_matrix(mel, "KINETIC ENERGY")
        logger.debug("KINETIC ENERGY %s", kinetic.shape)
        electronic_energy.kinetic = OneBodyElectronicIntegrals(ElectronicBasis.AO, (kinetic, None))

        overlap = GaussianDriver._get_matrix(mel, "OVERLAP")
        logger.debug("OVERLAP %s", overlap.shape)
        electronic_energy.overlap = OneBodyElectronicIntegrals(ElectronicBasis.AO, (overlap, None))

        orbs_energy = GaussianDriver._get_matrix(mel, "ALPHA ORBITAL ENERGIES")
        logger.debug("ORBITAL ENERGIES %s", overlap.shape)
        orbs_energy_b = GaussianDriver._get_matrix(mel, "BETA ORBITAL ENERGIES")
        logger.debug("BETA ORBITAL ENERGIES %s", overlap.shape)
        orbital_energies = (orbs_energy, orbs_energy_b) if moc_b is not None else orbs_energy
        electronic_energy.orbital_energies = np.asarray(orbital_energies)

        driver_result.add_property(electronic_energy)

        # dipole moment
        dipints = GaussianDriver._get_matrix(mel, "DIPOLE INTEGRALS")
        dipints = np.einsum("ijk->kji", dipints)

        x_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO, (dipints[0], None))
        y_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO, (dipints[1], None))
        z_dip_ints = OneBodyElectronicIntegrals(ElectronicBasis.AO, (dipints[2], None))

        x_dipole = DipoleMoment("x", [x_dip_ints, x_dip_ints.transform_basis(basis_transform)])
        y_dipole = DipoleMoment("y", [y_dip_ints, y_dip_ints.transform_basis(basis_transform)])
        z_dipole = DipoleMoment("z", [z_dip_ints, z_dip_ints.transform_basis(basis_transform)])

        nucl_dip = np.einsum("i,ix->x", mel.ian, coords)
        nucl_dip = np.round(nucl_dip, decimals=8)

        driver_result.add_property(
            ElectronicDipoleMoment(
                [x_dipole, y_dipole, z_dipole],
                nuclear_dipole_moment=nucl_dip,
                reverse_dipole_sign=True,
            )
        )

        # extra properties
        # TODO: once https://github.com/Qiskit/qiskit-nature/issues/312 is fixed we can stop adding
        # these properties by default.
        # if not settings.dict_aux_operators:
        driver_result.add_property(AngularMomentum(nmo * 2))
        driver_result.add_property(Magnetization(nmo * 2))

        return driver_result
 def setUp(self):
     """Setup."""
     super().setUp()
     num_molecular_orbitals = 4
     self.prop = AngularMomentum(num_molecular_orbitals * 2)