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)
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)