Esempio n. 1
0
    def _rotate_orbitals_in_qmolecule(
            qmolecule: QMolecule, orbital_rotation: 'OrbitalRotation') -> None:
        """
        Rotates the orbitals by applying a modified a anti-hermitian matrix
        (orbital_rotation.matrix_a) onto the MO coefficients matrix and recomputes all the
        quantities dependent on the MO coefficients. Be aware that qmolecule is modified
        when this executes.
        Args:
            qmolecule: instance of QMolecule class
            orbital_rotation: instance of OrbitalRotation class
        """

        # 1 and 2 electron integrals (required) from AO to MO basis
        qmolecule.mo_coeff = np.matmul(qmolecule.mo_coeff,
                                       orbital_rotation.matrix_a)
        qmolecule.mo_onee_ints = qmolecule.oneeints2mo(qmolecule.hcore,
                                                       qmolecule.mo_coeff)
        # support for unrestricted spins
        if qmolecule.mo_coeff_b is not None:
            qmolecule.mo_coeff_b = np.matmul(qmolecule.mo_coeff_b,
                                             orbital_rotation.matrix_b)
            qmolecule.mo_onee_ints_b = qmolecule.oneeints2mo(
                qmolecule.hcore, qmolecule.mo_coeff)

        qmolecule.mo_eri_ints = qmolecule.twoeints2mo(qmolecule.eri,
                                                      qmolecule.mo_coeff)
        if qmolecule.mo_coeff_b is not None:
            mo_eri_b = qmolecule.twoeints2mo(qmolecule.eri,
                                             qmolecule.mo_coeff_b)
            norbs = qmolecule.mo_coeff.shape[0]
            qmolecule.mo_eri_ints_bb = mo_eri_b.reshape(
                norbs, norbs, norbs, norbs)
            qmolecule.mo_eri_ints_ba = qmolecule.twoeints2mo_general(
                qmolecule.eri, qmolecule.mo_coeff_b, qmolecule.mo_coeff_b,
                qmolecule.mo_coeff, qmolecule.mo_coeff)
            qmolecule.mo_eri_ints_ba = qmolecule.mo_eri_ints_ba.reshape(
                norbs, norbs, norbs, norbs)
        # dipole integrals (if available) from AO to MO
        if qmolecule.x_dip_ints is not None:
            qmolecule.x_dip_mo_ints = qmolecule.oneeints2mo(
                qmolecule.x_dip_ints, qmolecule.mo_coeff)
            qmolecule.y_dip_mo_ints = qmolecule.oneeints2mo(
                qmolecule.y_dip_ints, qmolecule.mo_coeff)
            qmolecule.z_dip_mo_ints = qmolecule.oneeints2mo(
                qmolecule.z_dip_ints, qmolecule.mo_coeff)
        # support for unrestricted spins
        if qmolecule.mo_coeff_b is not None and qmolecule.x_dip_ints is not None:
            qmolecule.x_dip_mo_ints_b = qmolecule.oneeints2mo(
                qmolecule.x_dip_ints, qmolecule.mo_coeff_b)
            qmolecule.y_dip_mo_ints_b = qmolecule.oneeints2mo(
                qmolecule.y_dip_ints, qmolecule.mo_coeff_b)
            qmolecule.z_dip_mo_ints_b = qmolecule.oneeints2mo(
                qmolecule.z_dip_ints, qmolecule.mo_coeff_b)
Esempio n. 2
0
def qmol_func(mol, calc_type='rhf', atomic=False):
    ehf, enuke, norbs, mohij, mohijkl, mo_coeff, orbs_energy, x_dip, y_dip, z_dip, nucl_dip = _calculate_integrals(
        mol, calc_type, atomic)
    # Create driver level molecule object and populate
    _q_ = QMolecule()

    # Energies and orbits
    _q_.hf_energy = ehf
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_orbitals = norbs
    _q_.num_alpha = mol.nelec[0]
    _q_.num_beta = mol.nelec[1]
    _q_.mo_coeff = mo_coeff
    _q_.orbital_energies = orbs_energy
    # Molecule geometry
    _q_.molecular_charge = mol.charge
    _q_.multiplicity = mol.spin + 1
    _q_.num_atoms = mol.natm
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([mol.natm, 3])
    atoms = mol.atom_coords()
    for _n in range(0, _q_.num_atoms):
        xyz = mol.atom_coord(_n)
        _q_.atom_symbol.append(mol.atom_pure_symbol(_n))
        _q_.atom_xyz[_n][0] = xyz[0]
        _q_.atom_xyz[_n][1] = xyz[1]
        _q_.atom_xyz[_n][2] = xyz[2]
    # 1 and 2 electron integrals. h1 & h2 are ready to pass to FermionicOperator
    _q_.mo_onee_ints = mohij
    _q_.mo_eri_ints = mohijkl
    # dipole integrals
    _q_.x_dip_mo_ints = x_dip
    _q_.y_dip_mo_ints = y_dip
    _q_.z_dip_mo_ints = z_dip
    # dipole moment
    _q_.nuclear_dipole_moment = nucl_dip
    _q_.reverse_dipole_sign = True
    return _q_
Esempio n. 3
0
def _calculate_integrals(mol,
                         hf_method='rhf',
                         conv_tol=1e-9,
                         max_cycle=50,
                         init_guess='minao'):
    """Function to calculate the one and two electron terms. Perform a Hartree-Fock calculation in
        the given basis.
    Args:
        mol (gto.Mole) : A PySCF gto.Mole object.
        hf_method (str): rhf, uhf, rohf
        conv_tol (float): Convergence tolerance
        max_cycle (int): Max convergence cycles
        init_guess (str): Initial guess for SCF
    Returns:
        QMolecule: QMolecule populated with driver integrals etc
    """
    enuke = gto.mole.energy_nuc(mol)

    if hf_method == 'rhf':
        mf = scf.RHF(mol)
    elif hf_method == 'rohf':
        mf = scf.ROHF(mol)
    elif hf_method == 'uhf':
        mf = scf.UHF(mol)
    else:
        raise QiskitChemistryError(
            'Invalid hf_method type: {}'.format(hf_method))

    mf.conv_tol = conv_tol
    mf.max_cycle = max_cycle
    mf.init_guess = init_guess
    ehf = mf.kernel()
    logger.info('PySCF kernel() converged: {}, e(hf): {}'.format(
        mf.converged, mf.e_tot))
    if type(mf.mo_coeff) is tuple:
        mo_coeff = mf.mo_coeff[0]
        mo_coeff_B = mf.mo_coeff[1]
        # mo_occ   = mf.mo_occ[0]
        # mo_occ_B = mf.mo_occ[1]
    else:
        # With PySCF 1.6.2, instead of a tuple of 2 dimensional arrays, its a 3 dimensional
        # array with the first dimension indexing to the coeff arrays for alpha and beta
        if len(mf.mo_coeff.shape) > 2:
            mo_coeff = mf.mo_coeff[0]
            mo_coeff_B = mf.mo_coeff[1]
            # mo_occ   = mf.mo_occ[0]
            # mo_occ_B = mf.mo_occ[1]
        else:
            mo_coeff = mf.mo_coeff
            mo_coeff_B = None
            # mo_occ   = mf.mo_occ
            # mo_occ_B = None
    norbs = mo_coeff.shape[0]

    if type(mf.mo_energy) is tuple:
        orbs_energy = mf.mo_energy[0]
        orbs_energy_B = mf.mo_energy[1]
    else:
        # See PYSCF 1.6.2 comment above - this was similarly changed
        if len(mf.mo_energy.shape) > 1:
            orbs_energy = mf.mo_energy[0]
            orbs_energy_B = mf.mo_energy[1]
        else:
            orbs_energy = mf.mo_energy
            orbs_energy_B = None

    if logger.isEnabledFor(logging.DEBUG):
        # Add some more to PySCF output...
        # First analyze() which prints extra information about MO energy and occupation
        mol.stdout.write('\n')
        mf.analyze()
        # Now labelled orbitals for contributions to the MOs for s,p,d etc of each atom
        mol.stdout.write('\n\n--- Alpha Molecular Orbitals ---\n\n')
        dump_mat.dump_mo(mol, mo_coeff, digits=7, start=1)
        if mo_coeff_B is not None:
            mol.stdout.write('\n--- Beta Molecular Orbitals ---\n\n')
            dump_mat.dump_mo(mol, mo_coeff_B, digits=7, start=1)
        mol.stdout.flush()

    hij = mf.get_hcore()
    mohij = np.dot(np.dot(mo_coeff.T, hij), mo_coeff)
    mohij_B = None
    if mo_coeff_B is not None:
        mohij_B = np.dot(np.dot(mo_coeff_B.T, hij), mo_coeff_B)

    eri = mol.intor('int2e', aosym=1)
    mo_eri = ao2mo.incore.full(mf._eri, mo_coeff, compact=False)
    mohijkl = mo_eri.reshape(norbs, norbs, norbs, norbs)
    mohijkl_BB = None
    mohijkl_BA = None
    if mo_coeff_B is not None:
        mo_eri_B = ao2mo.incore.full(mf._eri, mo_coeff_B, compact=False)
        mohijkl_BB = mo_eri_B.reshape(norbs, norbs, norbs, norbs)
        mo_eri_BA = ao2mo.incore.general(
            mf._eri, (mo_coeff_B, mo_coeff_B, mo_coeff, mo_coeff),
            compact=False)
        mohijkl_BA = mo_eri_BA.reshape(norbs, norbs, norbs, norbs)

    # dipole integrals
    mol.set_common_orig((0, 0, 0))
    ao_dip = mol.intor_symmetric('int1e_r', comp=3)
    x_dip_ints = ao_dip[0]
    y_dip_ints = ao_dip[1]
    z_dip_ints = ao_dip[2]

    dm = mf.make_rdm1(mf.mo_coeff, mf.mo_occ)
    if hf_method == 'rohf' or hf_method == 'uhf':
        dm = dm[0]
    elec_dip = np.negative(np.einsum('xij,ji->x', ao_dip, dm).real)
    elec_dip = np.round(elec_dip, decimals=8)
    nucl_dip = np.einsum('i,ix->x', mol.atom_charges(), mol.atom_coords())
    nucl_dip = np.round(nucl_dip, decimals=8)
    logger.info("HF Electronic dipole moment: {}".format(elec_dip))
    logger.info("Nuclear dipole moment: {}".format(nucl_dip))
    logger.info("Total dipole moment: {}".format(nucl_dip + elec_dip))

    # Create driver level molecule object and populate
    _q_ = QMolecule()
    _q_.origin_driver_version = pyscf_version
    # Energies and orbits
    _q_.hf_energy = ehf
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_orbitals = norbs
    _q_.num_alpha = mol.nelec[0]
    _q_.num_beta = mol.nelec[1]
    _q_.mo_coeff = mo_coeff
    _q_.mo_coeff_B = mo_coeff_B
    _q_.orbital_energies = orbs_energy
    _q_.orbital_energies_B = orbs_energy_B
    # Molecule geometry
    _q_.molecular_charge = mol.charge
    _q_.multiplicity = mol.spin + 1
    _q_.num_atoms = mol.natm
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([mol.natm, 3])
    atoms = mol.atom_coords()
    for _n in range(0, _q_.num_atoms):
        xyz = mol.atom_coord(_n)
        _q_.atom_symbol.append(mol.atom_pure_symbol(_n))
        _q_.atom_xyz[_n][0] = xyz[0]
        _q_.atom_xyz[_n][1] = xyz[1]
        _q_.atom_xyz[_n][2] = xyz[2]
    # 1 and 2 electron integrals AO and MO
    _q_.hcore = hij
    _q_.hcore_B = None
    _q_.kinetic = mol.intor_symmetric('int1e_kin')
    _q_.overlap = mf.get_ovlp()
    _q_.eri = eri
    _q_.mo_onee_ints = mohij
    _q_.mo_onee_ints_B = mohij_B
    _q_.mo_eri_ints = mohijkl
    _q_.mo_eri_ints_BB = mohijkl_BB
    _q_.mo_eri_ints_BA = mohijkl_BA
    # dipole integrals AO and MO
    _q_.x_dip_ints = x_dip_ints
    _q_.y_dip_ints = y_dip_ints
    _q_.z_dip_ints = z_dip_ints
    _q_.x_dip_mo_ints = QMolecule.oneeints2mo(x_dip_ints, mo_coeff)
    _q_.x_dip_mo_ints_B = None
    _q_.y_dip_mo_ints = QMolecule.oneeints2mo(y_dip_ints, mo_coeff)
    _q_.y_dip_mo_ints_B = None
    _q_.z_dip_mo_ints = QMolecule.oneeints2mo(z_dip_ints, mo_coeff)
    _q_.z_dip_mo_ints_B = None
    if mo_coeff_B is not None:
        _q_.x_dip_mo_ints_B = QMolecule.oneeints2mo(x_dip_ints, mo_coeff_B)
        _q_.y_dip_mo_ints_B = QMolecule.oneeints2mo(y_dip_ints, mo_coeff_B)
        _q_.z_dip_mo_ints_B = QMolecule.oneeints2mo(z_dip_ints, mo_coeff_B)
    # dipole moment
    _q_.nuclear_dipole_moment = nucl_dip
    _q_.reverse_dipole_sign = True

    return _q_
Esempio n. 4
0
def compute_integrals(atom,
                      unit,
                      charge,
                      spin,
                      basis,
                      hf_method='rhf',
                      max_memory=None):
    # Get config from input parameters
    # molecule is in PySCF atom string format e.g. "H .0 .0 .0; H .0 .0 0.2"
    #          or in Z-Matrix format e.g. "H; O 1 1.08; H 2 1.08 1 107.5"
    # other parameters are as per PySCF got.Mole format

    atom = _check_molecule_format(atom)
    hf_method = hf_method.lower()
    if max_memory is None:
        max_memory = param.MAX_MEMORY

    try:
        mol = gto.Mole(atom=atom,
                       unit=unit,
                       basis=basis,
                       max_memory=max_memory,
                       verbose=pylogger.QUIET)
        mol.symmetry = False
        mol.charge = charge
        mol.spin = spin
        mol.build(parse_arg=False)
        ehf, enuke, norbs, mohij, mohijkl, mo_coeff, orbs_energy, x_dip, y_dip, z_dip, nucl_dip = _calculate_integrals(
            mol, hf_method)
    except Exception as exc:
        raise QiskitChemistryError(
            'Failed electronic structure computation') from exc

    # Create driver level molecule object and populate
    _q_ = QMolecule()
    # Energies and orbits
    _q_.hf_energy = ehf
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_orbitals = norbs
    _q_.num_alpha = mol.nelec[0]
    _q_.num_beta = mol.nelec[1]
    _q_.mo_coeff = mo_coeff
    _q_.orbital_energies = orbs_energy
    # Molecule geometry
    _q_.molecular_charge = mol.charge
    _q_.multiplicity = mol.spin + 1
    _q_.num_atoms = mol.natm
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([mol.natm, 3])
    atoms = mol.atom_coords()
    for _n in range(0, _q_.num_atoms):
        xyz = mol.atom_coord(_n)
        _q_.atom_symbol.append(mol.atom_pure_symbol(_n))
        _q_.atom_xyz[_n][0] = xyz[0]
        _q_.atom_xyz[_n][1] = xyz[1]
        _q_.atom_xyz[_n][2] = xyz[2]
    # 1 and 2 electron integrals. h1 & h2 are ready to pass to FermionicOperator
    _q_.mo_onee_ints = mohij
    _q_.mo_eri_ints = mohijkl
    # dipole integrals
    _q_.x_dip_mo_ints = x_dip
    _q_.y_dip_mo_ints = y_dip
    _q_.z_dip_mo_ints = z_dip
    # dipole moment
    _q_.nuclear_dipole_moment = nucl_dip
    _q_.reverse_dipole_sign = True

    return _q_
Esempio n. 5
0
    def _parse_matrix_file(self, fname, useAO2E=False):
        # 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)

            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 QiskitChemistryError(msg)

        mel = MatEl(file=fname)
        logger.debug('MatrixElement file:\n{}'.format(mel))

        # Create driver level molecule object and populate
        _q_ = QMolecule()
        # Energies and orbits
        _q_.hf_energy = mel.scalar('ETOTAL')
        _q_.nuclear_repulsion_energy = mel.scalar('ENUCREP')
        _q_.num_orbitals = 0  # updated below from orbital coeffs size
        _q_.num_alpha = (mel.ne + mel.multip - 1) // 2
        _q_.num_beta = (mel.ne - mel.multip + 1) // 2
        _q_.molecular_charge = mel.icharg
        # Molecule geometry
        _q_.multiplicity = mel.multip
        _q_.num_atoms = mel.natoms
        _q_.atom_symbol = []
        _q_.atom_xyz = np.empty([mel.natoms, 3])
        syms = mel.ian
        xyz = np.reshape(mel.c, (_q_.num_atoms, 3))
        for _n in range(0, _q_.num_atoms):
            _q_.atom_symbol.append(QMolecule.symbols[syms[_n]])
            for _i in range(xyz.shape[1]):
                coord = xyz[_n][_i]
                if abs(coord) < 1e-10:
                    coord = 0
                _q_.atom_xyz[_n][_i] = coord

        moc = self._getMatrix(mel, 'ALPHA MO COEFFICIENTS')
        _q_.num_orbitals = moc.shape[0]
        _q_.mo_coeff = moc
        orbs_energy = self._getMatrix(mel, 'ALPHA ORBITAL ENERGIES')
        _q_.orbital_energies = orbs_energy

        # 1 and 2 electron integrals
        hcore = self._getMatrix(mel, 'CORE HAMILTONIAN ALPHA')
        logger.debug('CORE HAMILTONIAN ALPHA {}'.format(hcore.shape))
        mohij = QMolecule.oneeints2mo(hcore, moc)
        if useAO2E:
            # These are 2-body in AO. We can convert to MO via the QMolecule
            # method but using ints in MO already, as in the else here, is better
            eri = self._getMatrix(mel, 'REGULAR 2E INTEGRALS')
            logger.debug('REGULAR 2E INTEGRALS {}'.format(eri.shape))
            mohijkl = QMolecule.twoeints2mo(eri, moc)
        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 = self._getMatrix(mel, 'AA MO 2E INTEGRALS')
            logger.debug('AA MO 2E INTEGRALS {}'.format(mohijkl.shape))

        _q_.mo_onee_ints = mohij
        _q_.mo_eri_ints = mohijkl

        # dipole moment
        dipints = self._getMatrix(mel, 'DIPOLE INTEGRALS')
        dipints = np.einsum('ijk->kji', dipints)
        _q_.x_dip_mo_ints = QMolecule.oneeints2mo(dipints[0], moc)
        _q_.y_dip_mo_ints = QMolecule.oneeints2mo(dipints[1], moc)
        _q_.z_dip_mo_ints = QMolecule.oneeints2mo(dipints[2], moc)

        nucl_dip = np.einsum('i,ix->x', syms, xyz)
        nucl_dip = np.round(nucl_dip, decimals=8)
        _q_.nuclear_dipole_moment = nucl_dip
        _q_.reverse_dipole_sign = True

        return _q_
Esempio n. 6
0
    def _parse_matrix_file(self, fname, useao2e=False):
        # 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 QiskitChemistryError(msg) from mnfe

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

        # Create driver level molecule object and populate
        _q_ = QMolecule()
        _q_.origin_driver_version = mel.gversion
        # Energies and orbits
        _q_.hf_energy = mel.scalar('ETOTAL')
        _q_.nuclear_repulsion_energy = mel.scalar('ENUCREP')
        _q_.num_orbitals = 0  # updated below from orbital coeffs size
        _q_.num_alpha = (mel.ne + mel.multip - 1) // 2
        _q_.num_beta = (mel.ne - mel.multip + 1) // 2
        moc = self._get_matrix(mel, 'ALPHA MO COEFFICIENTS')
        moc_b = self._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
        _q_.num_orbitals = moc.shape[0]
        _q_.mo_coeff = moc
        _q_.mo_coeff_b = moc_b
        orbs_energy = self._get_matrix(mel, 'ALPHA ORBITAL ENERGIES')
        _q_.orbital_energies = orbs_energy
        orbs_energy_b = self._get_matrix(mel, 'BETA ORBITAL ENERGIES')
        _q_.orbital_energies_b = orbs_energy_b if moc_b is not None else None
        # Molecule geometry
        _q_.molecular_charge = mel.icharg
        _q_.multiplicity = mel.multip
        _q_.num_atoms = mel.natoms
        _q_.atom_symbol = []
        _q_.atom_xyz = np.empty([mel.natoms, 3])
        syms = mel.ian
        xyz = np.reshape(mel.c, (_q_.num_atoms, 3))
        for n_i in range(0, _q_.num_atoms):
            _q_.atom_symbol.append(QMolecule.symbols[syms[n_i]])
            for idx in range(xyz.shape[1]):
                coord = xyz[n_i][idx]
                if abs(coord) < 1e-10:
                    coord = 0
                _q_.atom_xyz[n_i][idx] = coord

        # 1 and 2 electron integrals
        hcore = self._get_matrix(mel, 'CORE HAMILTONIAN ALPHA')
        logger.debug('CORE HAMILTONIAN ALPHA %s', hcore.shape)
        hcore_b = self._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)
        kinetic = self._get_matrix(mel, 'KINETIC ENERGY')
        logger.debug('KINETIC ENERGY %s', kinetic.shape)
        overlap = self._get_matrix(mel, 'OVERLAP')
        logger.debug('OVERLAP %s', overlap.shape)
        mohij = QMolecule.oneeints2mo(hcore, moc)
        mohij_b = None
        if moc_b is not None:
            mohij_b = QMolecule.oneeints2mo(hcore if hcore_b is None else hcore_b, moc_b)

        eri = self._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')

        if useao2e:
            # eri are 2-body in AO. We can convert to MO via the QMolecule
            # method but using ints in MO already, as in the else here, is better
            mohijkl = QMolecule.twoeints2mo(eri, moc)
            mohijkl_bb = None
            mohijkl_ba = None
            if moc_b is not None:
                mohijkl_bb = QMolecule.twoeints2mo(eri, moc_b)
                mohijkl_ba = QMolecule.twoeints2mo_general(eri, moc_b, moc_b, moc, moc)
        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 = self._get_matrix(mel, 'AA MO 2E INTEGRALS')
            logger.debug('AA MO 2E INTEGRALS %s', mohijkl.shape)
            mohijkl_bb = self._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 = self._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)

        _q_.hcore = hcore
        _q_.hcore_b = hcore_b
        _q_.kinetic = kinetic
        _q_.overlap = overlap
        _q_.eri = eri

        _q_.mo_onee_ints = mohij
        _q_.mo_onee_ints_b = mohij_b
        _q_.mo_eri_ints = mohijkl
        _q_.mo_eri_ints_bb = mohijkl_bb
        _q_.mo_eri_ints_ba = mohijkl_ba

        # dipole moment
        dipints = self._get_matrix(mel, 'DIPOLE INTEGRALS')
        dipints = np.einsum('ijk->kji', dipints)
        _q_.x_dip_ints = dipints[0]
        _q_.y_dip_ints = dipints[1]
        _q_.z_dip_ints = dipints[2]
        _q_.x_dip_mo_ints = QMolecule.oneeints2mo(dipints[0], moc)
        _q_.x_dip_mo_ints_b = None
        _q_.y_dip_mo_ints = QMolecule.oneeints2mo(dipints[1], moc)
        _q_.y_dip_mo_ints_b = None
        _q_.z_dip_mo_ints = QMolecule.oneeints2mo(dipints[2], moc)
        _q_.z_dip_mo_ints_b = None
        if moc_b is not None:
            _q_.x_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[0], moc_b)
            _q_.y_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[1], moc_b)
            _q_.z_dip_mo_ints_b = QMolecule.oneeints2mo(dipints[2], moc_b)

        nucl_dip = np.einsum('i,ix->x', syms, xyz)
        nucl_dip = np.round(nucl_dip, decimals=8)
        _q_.nuclear_dipole_moment = nucl_dip
        _q_.reverse_dipole_sign = True

        return _q_
Esempio n. 7
0
def _calculate_integrals(mol, hf_method='rhf', conv_tol=1e-9, max_cycle=50, init_guess='minao',outfile=None):
    """Function to calculate the one and two electron terms. Perform a Hartree-Fock calculation in
        the given basis.
    Args:
        mol (gto.Mole) : A PySCF gto.Mole object.
        hf_method (str): rhf, uhf, rohf
        conv_tol (float): Convergence tolerance
        max_cycle (int): Max convergence cycles
        init_guess (str): Initial guess for SCF
    Returns:
        QMolecule: QMolecule populated with driver integrals etc
    Raises:
        QiskitChemistryError: Invalid hf method type
    """
    enuke = gto.mole.energy_nuc(mol)

    if hf_method == 'rhf':
        m_f = scf.RHF(mol)
    elif hf_method == 'rohf':
        m_f = scf.ROHF(mol)
    elif hf_method == 'uhf':
        m_f = scf.UHF(mol)
    else:
        raise QiskitChemistryError('Invalid hf_method type: {}'.format(hf_method))

    m_f.conv_tol = conv_tol
    m_f.max_cycle = max_cycle
    m_f.init_guess = init_guess
    ehf = m_f.kernel()

    from pyscf import tools

    from prettytable import PrettyTable
    C = m_f.mo_coeff

    irr = get_irreps(mol,C)
    table_ancillary_info = [[str(round(m_f.mo_energy[i],4)),irr[i],str(int(m_f.mo_occ[i]))] for i in range(mol.nao_nr())]

    outfile.write("SCF orbitals\n")
    t = PrettyTable(['MO']+mol.ao_labels()+['E','irr','occ'])
    for i in range(C.shape[1]):
        if(C[np.argmax(np.abs(C[:,i])),i]<0): C[:,i] *= -1
        t.add_row([str(i)]+[str(round(x,4)) for x in C[:,i]]+table_ancillary_info[i])
    outfile.write(str(t))

    filename = 'mos'
    for i in range(m_f.mo_coeff.shape[1]):
        moldenfile = filename+'-'+str(i)+'.molden'
        tools.molden.from_mo(mol,moldenfile,m_f.mo_coeff)
        jmol_script = filename+'-'+str(i)+'.spt'
        fspt = open(jmol_script,'w')
        fspt.write('''
        initialize;
        set background [xffffff];
        set frank off
        set autoBond true;
        set bondRadiusMilliAngstroms 66;
        set bondTolerance 0.5;
        set forceAutoBond false;
        load %s
        ''' % moldenfile)
        fspt.write('''
        zoom 130;
        rotate -20 z
        rotate -60 x
        axes
        MO COLOR [xff0020] [x0060ff];
        MO COLOR translucent 0.25;
        MO fill noDots noMesh;
        MO titleformat "";
        ''')
        fspt.write('MO %d cutoff 0.02;\n' % (i+1))
        fspt.write('write IMAGE 400 400 PNG 180 "%s-%02d.png";\n' % (filename,i+1))
        fspt.close()

    logger.info('PySCF kernel() converged: %s, e(hf): %s', m_f.converged, m_f.e_tot)
    if isinstance(m_f.mo_coeff, tuple):
        mo_coeff = m_f.mo_coeff[0]
        mo_coeff_b = m_f.mo_coeff[1]
        # mo_occ   = m_f.mo_occ[0]
        # mo_occ_b = m_f.mo_occ[1]
    else:
        # With PySCF 1.6.2, instead of a tuple of 2 dimensional arrays, its a 3 dimensional
        # array with the first dimension indexing to the coeff arrays for alpha and beta
        if len(m_f.mo_coeff.shape) > 2:
            mo_coeff = m_f.mo_coeff[0]
            mo_coeff_b = m_f.mo_coeff[1]
            # mo_occ   = m_f.mo_occ[0]
            # mo_occ_b = m_f.mo_occ[1]
        else:
            mo_coeff = m_f.mo_coeff
            mo_coeff_b = None
            # mo_occ   = mf.mo_occ
            # mo_occ_b = None
    norbs = mo_coeff.shape[0]

    if isinstance(m_f.mo_energy, tuple):
        orbs_energy = m_f.mo_energy[0]
        orbs_energy_b = m_f.mo_energy[1]
    else:
        # See PYSCF 1.6.2 comment above - this was similarly changed
        if len(m_f.mo_energy.shape) > 1:
            orbs_energy = m_f.mo_energy[0]
            orbs_energy_b = m_f.mo_energy[1]
        else:
            orbs_energy = m_f.mo_energy
            orbs_energy_b = None

    if logger.isEnabledFor(logging.DEBUG):
        # Add some more to PySCF output...
        # First analyze() which prints extra information about MO energy and occupation
        mol.stdout.write('\n')
        m_f.analyze()
        # Now labelled orbitals for contributions to the MOs for s,p,d etc of each atom
        mol.stdout.write('\n\n--- Alpha Molecular Orbitals ---\n\n')
        dump_mat.dump_mo(mol, mo_coeff, digits=7, start=1)
        if mo_coeff_b is not None:
            mol.stdout.write('\n--- Beta Molecular Orbitals ---\n\n')
            dump_mat.dump_mo(mol, mo_coeff_b, digits=7, start=1)
        mol.stdout.flush()

    hij = m_f.get_hcore()
    mohij = np.dot(np.dot(mo_coeff.T, hij), mo_coeff)
    mohij_b = None
    if mo_coeff_b is not None:
        mohij_b = np.dot(np.dot(mo_coeff_b.T, hij), mo_coeff_b)

    eri = mol.intor('int2e', aosym=1)
    mo_eri = ao2mo.incore.full(m_f._eri, mo_coeff, compact=False)
    mohijkl = mo_eri.reshape(norbs, norbs, norbs, norbs)
    mohijkl_bb = None
    mohijkl_ba = None
    if mo_coeff_b is not None:
        mo_eri_b = ao2mo.incore.full(m_f._eri, mo_coeff_b, compact=False)
        mohijkl_bb = mo_eri_b.reshape(norbs, norbs, norbs, norbs)
        mo_eri_ba = ao2mo.incore.general(m_f._eri,
                                         (mo_coeff_b, mo_coeff_b, mo_coeff, mo_coeff),
                                         compact=False)
        mohijkl_ba = mo_eri_ba.reshape(norbs, norbs, norbs, norbs)

    # dipole integrals
    mol.set_common_orig((0, 0, 0))
    ao_dip = mol.intor_symmetric('int1e_r', comp=3)
    x_dip_ints = ao_dip[0]
    y_dip_ints = ao_dip[1]
    z_dip_ints = ao_dip[2]

    d_m = m_f.make_rdm1(m_f.mo_coeff, m_f.mo_occ)
    if hf_method in ('rohf', 'uhf'):
        d_m = d_m[0]
    elec_dip = np.negative(np.einsum('xij,ji->x', ao_dip, d_m).real)
    elec_dip = np.round(elec_dip, decimals=8)
    nucl_dip = np.einsum('i,ix->x', mol.atom_charges(), mol.atom_coords())
    nucl_dip = np.round(nucl_dip, decimals=8)
    logger.info("HF Electronic dipole moment: %s", elec_dip)
    logger.info("Nuclear dipole moment: %s", nucl_dip)
    logger.info("Total dipole moment: %s", nucl_dip+elec_dip)

    # Create driver level molecule object and populate
    _q_ = QMolecule()
    _q_.origin_driver_version = pyscf_version
    # Energies and orbits
    _q_.hf_energy = ehf
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_orbitals = norbs
    _q_.num_alpha = mol.nelec[0]
    _q_.num_beta = mol.nelec[1]
    _q_.mo_coeff = mo_coeff
    _q_.mo_coeff_b = mo_coeff_b
    _q_.orbital_energies = orbs_energy
    _q_.orbital_energies_b = orbs_energy_b
    # Molecule geometry
    _q_.molecular_charge = mol.charge
    _q_.multiplicity = mol.spin + 1
    _q_.num_atoms = mol.natm
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([mol.natm, 3])
    _ = mol.atom_coords()
    for n_i in range(0, _q_.num_atoms):
        xyz = mol.atom_coord(n_i)
        _q_.atom_symbol.append(mol.atom_pure_symbol(n_i))
        _q_.atom_xyz[n_i][0] = xyz[0]
        _q_.atom_xyz[n_i][1] = xyz[1]
        _q_.atom_xyz[n_i][2] = xyz[2]
    # 1 and 2 electron integrals AO and MO
    _q_.hcore = hij
    _q_.hcore_b = None
    _q_.kinetic = mol.intor_symmetric('int1e_kin')
    _q_.overlap = m_f.get_ovlp()
    _q_.eri = eri
    _q_.mo_onee_ints = mohij
    _q_.mo_onee_ints_b = mohij_b
    _q_.mo_eri_ints = mohijkl
    _q_.mo_eri_ints_bb = mohijkl_bb
    _q_.mo_eri_ints_ba = mohijkl_ba
    # dipole integrals AO and MO
    _q_.x_dip_ints = x_dip_ints
    _q_.y_dip_ints = y_dip_ints
    _q_.z_dip_ints = z_dip_ints
    _q_.x_dip_mo_ints = QMolecule.oneeints2mo(x_dip_ints, mo_coeff)
    _q_.x_dip_mo_ints_b = None
    _q_.y_dip_mo_ints = QMolecule.oneeints2mo(y_dip_ints, mo_coeff)
    _q_.y_dip_mo_ints_b = None
    _q_.z_dip_mo_ints = QMolecule.oneeints2mo(z_dip_ints, mo_coeff)
    _q_.z_dip_mo_ints_b = None
    if mo_coeff_b is not None:
        _q_.x_dip_mo_ints_b = QMolecule.oneeints2mo(x_dip_ints, mo_coeff_b)
        _q_.y_dip_mo_ints_b = QMolecule.oneeints2mo(y_dip_ints, mo_coeff_b)
        _q_.z_dip_mo_ints_b = QMolecule.oneeints2mo(z_dip_ints, mo_coeff_b)
    # dipole moment
    _q_.nuclear_dipole_moment = nucl_dip
    _q_.reverse_dipole_sign = True

    return _q_