Пример #1
0
def uhf_simple(geo,basisname='sto3g',maxiter=25,verbose=False):
    if geo.nopen() == 0: return scf_simple(geo,basisname,maxiter,verbose)
    bfs = basisset(geo,basisname)
    i1 = onee_integrals(bfs,geo)
    i2 = twoe_integrals(bfs)
    h = i1.T + i1.V
    E,U = geigh(h,i1.S)
    Enuke = geo.nuclear_repulsion()
    Eold = Energy = 0
    ca = cb = U
    na,nb = geo.nup(),geo.ndown()

    for i in xrange(maxiter):
        Energy = Enuke
        Da = dmat(ca,na)
        Db = dmat(cb,nb)
        h = i1.T + i1.V
        Energy += trace2(Da+Db,h)/2
        Ja,Ka = i2.get_j(Da),i2.get_k(Da)
        Jb,Kb = i2.get_j(Db),i2.get_k(Db)
        Fa = h + Ja + Jb - Ka
        Fb = h + Ja + Jb - Kb
        orbea,ca = geigh(Fa,i1.S)
        orbeb,cb = geigh(Fb,i1.S)
        Energy += trace2(Fa,Da)/2 + trace2(Fb,Db)/2
        print ("UHF: %d   %10.4f : %10.4f" % ((i+1),Energy,Enuke))
        if np.isclose(Energy,Eold):
            break
        Eold = Energy
    else:
        print ("Warning: Maxiter %d hit in scf_simple" % maxiter)
    return Energy,E,U
Пример #2
0
def uhf_simple(geo,basisname='sto3g',maxiter=25,verbose=False):
    if geo.nopen() == 0: return scf_simple(geo,basisname,maxiter,verbose)
    bfs = basisset(geo,basisname)
    i1 = onee_integrals(bfs,geo)
    i2 = twoe_integrals(bfs)
    h = i1.T + i1.V
    E,U = geigh(h,i1.S)
    Enuke = geo.nuclear_repulsion()
    Eold = Energy = 0
    ca = cb = U
    na,nb = geo.nup(),geo.ndown()

    for i in xrange(maxiter):
        Energy = Enuke
        Da = dmat(ca,na)
        Db = dmat(cb,nb)
        h = i1.T + i1.V
        Energy += trace2(Da+Db,h)/2
        Ja,Ka = i2.get_j(Da),i2.get_k(Da)
        Jb,Kb = i2.get_j(Db),i2.get_k(Db)
        Fa = h + Ja + Jb - Ka
        Fb = h + Ja + Jb - Kb
        orbea,ca = geigh(Fa,i1.S)
        orbeb,cb = geigh(Fb,i1.S)
        Energy += trace2(Fa,Da)/2 + trace2(Fb,Db)/2
        print ("UHF: %d   %10.4f : %10.4f" % ((i+1),Energy,Enuke))
        if np.isclose(Energy,Eold):
            break
        Eold = Energy
    else:
        print ("Warning: Maxiter %d hit in scf_simple" % maxiter)
    return Energy,E,U
Пример #3
0
def scf_simple(geo,basisname='sto-3g',maxiter=25,verbose=False):
    bfs = basisset(geo,basisname)
    i1 = onee_integrals(bfs,geo)
    i2 = twoe_integrals(bfs)
    if verbose: print ("S=\n%s" % i1.S)
    h = i1.T + i1.V
    if verbose: print ("h=\n%s" % h)
    if verbose: print ("T=\n%s" % i1.T)
    if verbose: print ("V=\n%s" % i1.V)
    E,U = geigh(h,i1.S)
    if verbose: print ("E=\n%s" % E)
    if verbose: print ("U=\n%s" % U)
    Enuke = geo.nuclear_repulsion()
    nocc = geo.nocc()
    Eold = Energy = 0
    if verbose: print ("2e ints\n%s" % i2)
    for i in xrange(maxiter):
        D = dmat(U,nocc)
        if verbose: print ("D=\n%s" % D)
        Eone = trace2(h,D)
        G = i2.get_2jk(D)
        H = h+G
        Etwo = trace2(H,D)
        E,U = geigh(H,i1.S)
        Energy = Enuke+Eone+Etwo
        print ("HF: %d   %10.4f : %10.4f %10.4f %10.4f" % ((i+1),Energy,Enuke,Eone,Etwo))
        if np.isclose(Energy,Eold):
            break
        Eold = Energy
    else:
        print ("Warning: Maxiter %d hit in scf_simple" % maxiter)
    return Energy,E,U
Пример #4
0
def rhf_simple(geo,basisname='sto3g',maxiter=25,verbose=False):
    bfs = basisset(geo,basisname)
    i1 = onee_integrals(bfs,geo)
    i2 = twoe_integrals(bfs)
    if verbose: print ("S=\n%s" % i1.S)
    h = i1.T + i1.V
    if verbose: print ("h=\n%s" % h)
    if verbose: print ("T=\n%s" % i1.T)
    if verbose: print ("V=\n%s" % i1.V)
    E,U = geigh(h,i1.S)
    if verbose: print ("E=\n%s" % E)
    if verbose: print ("U=\n%s" % U)
    Enuke = geo.nuclear_repulsion()
    nocc = geo.nocc()
    Eold = Energy = 0
    if verbose: print ("2e ints\n%s" % i2)
    for i in xrange(maxiter):
        D = dmat(U,nocc)
        if verbose: print ("D=\n%s" % D)
        Eone = trace2(h,D)
        G = i2.get_2jk(D)
        H = h+G
        Etwo = trace2(H,D)
        E,U = geigh(H,i1.S)
        Energy = Enuke+Eone+Etwo
        print ("HF: %d   %10.4f : %10.4f %10.4f %10.4f" % ((i+1),Energy,Enuke,Eone,Etwo))
        if np.isclose(Energy,Eold):
            break
        Eold = Energy
    else:
        print ("Warning: Maxiter %d hit in scf_simple" % maxiter)
    return Energy,E,U
Пример #5
0
 def __init__(self,geo,bfs):
     self.geo = geo
     self.bfs = bfs
     self.i1 = onee_integrals(bfs,geo)
     self.i2 = twoe_integrals(bfs)
     self.energies = []
     self.energy = 0
     self.converged = False
Пример #6
0
 def __init__(self,geo,bfs):
     self.geo = geo
     self.bfs = bfs
     self.i1 = onee_integrals(bfs,geo)
     self.i2 = twoe_integrals(bfs)
     self.energies = []
     self.energy = 0
     self.converged = False
Пример #7
0
def _calculate_integrals(molecule, basis='sto3g', calc_type='rhf'):
    """Function to calculate the one and two electron terms. Perform a Hartree-Fock calculation in
        the given basis.
    Args:
        molecule : A pyquante2 molecular object.
        basis : The basis set for the electronic structure computation
        calc_type: rhf, uhf, rohf
    Returns:
        ehf : Hartree-Fock energy
        enuke: Nuclear repulsion energy
        norbs : Number of orbitals
        mohij : One electron terms of the Hamiltonian.
        mohijkl : Two electron terms of the Hamiltonian.
        orbs: Molecular orbital coefficients
        orbs_energy: Orbital energies
    """
    bfs = basisset(molecule, basis)
    integrals = onee_integrals(bfs, molecule)
    hij = integrals.T + integrals.V
    hijkl_compressed = twoe_integrals(bfs)

    # convert overlap integrals to molecular basis
    # calculate the Hartree-Fock solution of the molecule

    if calc_type == 'rhf':
        solver = rhf(molecule, bfs)
    elif calc_type == 'rohf':
        solver = rohf(molecule, bfs)
    elif calc_type == 'uhf':
        solver = uhf(molecule, bfs)
    else:
        raise QiskitChemistryError('Invalid calc_type: {}'.format(calc_type))
    logger.debug('Solver name {}'.format(solver.name))
    ehf = solver.converge()
    if hasattr(solver, 'orbs'):
        orbs = solver.orbs
    else:
        orbs = solver.orbsa
    norbs = len(orbs)
    if hasattr(solver, 'orbe'):
        orbs_energy = solver.orbe
    else:
        orbs_energy = solver.orbea
    enuke = molecule.nuclear_repulsion()
    # Get ints in molecular orbital basis
    mohij = simx(hij, orbs)
    mohijkl_compressed = transformintegrals(hijkl_compressed, orbs)
    mohijkl = np.zeros((norbs, norbs, norbs, norbs))
    for i in range(norbs):
        for j in range(norbs):
            for k in range(norbs):
                for l in range(norbs):
                    mohijkl[i, j, k,
                            l] = mohijkl_compressed[ijkl2intindex(i, j, k, l)]

    return ehf[0], enuke, norbs, mohij, mohijkl, orbs, orbs_energy
Пример #8
0
    def _populate_driver_result_electronic_energy(
            self, driver_result: ElectronicStructureDriverResult) -> None:
        # pylint: disable=import-error
        from pyquante2 import onee_integrals
        from pyquante2.ints.integrals import twoe_integrals

        basis_transform = driver_result.get_property(ElectronicBasisTransform)

        integrals = onee_integrals(self._bfs, self._mol)

        hij = integrals.T + integrals.V
        hijkl = twoe_integrals(self._bfs)

        one_body_ao = OneBodyElectronicIntegrals(ElectronicBasis.AO,
                                                 (hij, None))

        two_body_ao = TwoBodyElectronicIntegrals(
            ElectronicBasis.AO,
            (hijkl.transform(np.identity(self._nmo)), None, None, None),
        )

        one_body_mo = one_body_ao.transform_basis(basis_transform)
        two_body_mo = two_body_ao.transform_basis(basis_transform)

        electronic_energy = ElectronicEnergy(
            [one_body_ao, two_body_ao, one_body_mo, two_body_mo],
            nuclear_repulsion_energy=self._mol.nuclear_repulsion(),
            reference_energy=self._calc.energy,
        )

        if hasattr(self._calc, "orbe"):
            orbs_energy = self._calc.orbe
            orbs_energy_b = None
        else:
            orbs_energy = self._calc.orbea
            orbs_energy_b = self._calc.orbeb

        orbital_energies = ((orbs_energy, orbs_energy_b)
                            if orbs_energy_b is not None else orbs_energy)
        electronic_energy.orbital_energies = np.asarray(orbital_energies)

        electronic_energy.kinetic = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (integrals.T, None))
        electronic_energy.overlap = OneBodyElectronicIntegrals(
            ElectronicBasis.AO, (integrals.S, None))

        driver_result.add_property(electronic_energy)
Пример #9
0
def _calculate_integrals(molecule,
                         basis="sto3g",
                         hf_method="rhf",
                         tol=1e-8,
                         maxiters=100):
    """Function to calculate the one and two electron terms. Perform a Hartree-Fock calculation in
        the given basis.
    Args:
        molecule (pyQuante2.molecule): A pyquante2 molecular object.
        basis (str) : The basis set for the electronic structure computation
        hf_method (str): rhf, uhf, rohf
        tol (float): tolerance
        maxiters (int): max. iterations
    Returns:
        QMolecule: QMolecule populated with driver integrals etc
    Raises:
        QiskitNatureError: Invalid hf methods type
    """
    bfs = basisset(molecule, basis)
    integrals = onee_integrals(bfs, molecule)
    hij = integrals.T + integrals.V
    hijkl = twoe_integrals(bfs)

    # convert overlap integrals to molecular basis
    # calculate the Hartree-Fock solution of the molecule

    if hf_method == "rhf":
        solver = rhf(molecule, bfs)
    elif hf_method == "rohf":
        solver = rohf(molecule, bfs)
    elif hf_method == "uhf":
        solver = uhf(molecule, bfs)
    else:
        raise QiskitNatureError("Invalid hf_method type: {}".format(hf_method))
    ehf = solver.converge(tol=tol, maxiters=maxiters)
    logger.debug("PyQuante2 processing information:\n%s", solver)
    if hasattr(solver, "orbs"):
        orbs = solver.orbs
        orbs_b = None
    else:
        orbs = solver.orbsa
        orbs_b = solver.orbsb
    norbs = len(orbs)
    if hasattr(solver, "orbe"):
        orbs_energy = solver.orbe
        orbs_energy_b = None
    else:
        orbs_energy = solver.orbea
        orbs_energy_b = solver.orbeb
    enuke = molecule.nuclear_repulsion()
    # Get ints in molecular orbital basis
    mohij = simx(hij, orbs)
    mohij_b = None
    if orbs_b is not None:
        mohij_b = simx(hij, orbs_b)

    eri = hijkl.transform(np.identity(norbs))
    mohijkl = hijkl.transform(orbs)
    mohijkl_bb = None
    mohijkl_ba = None
    if orbs_b is not None:
        mohijkl_bb = hijkl.transform(orbs_b)
        mohijkl_ba = np.einsum("aI,bJ,cK,dL,abcd->IJKL", orbs_b, orbs_b, orbs,
                               orbs, hijkl[...])

    # Create driver level molecule object and populate
    _q_ = QMolecule()
    _q_.origin_driver_version = "?"  # No version info seems available to access
    # Energies and orbits
    _q_.hf_energy = ehf[0]
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_molecular_orbitals = norbs
    _q_.num_alpha = molecule.nup()
    _q_.num_beta = molecule.ndown()
    _q_.mo_coeff = orbs
    _q_.mo_coeff_b = orbs_b
    _q_.orbital_energies = orbs_energy
    _q_.orbital_energies_b = orbs_energy_b
    # Molecule geometry
    _q_.molecular_charge = molecule.charge
    _q_.multiplicity = molecule.multiplicity
    _q_.num_atoms = len(molecule)
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([len(molecule), 3])
    atoms = molecule.atoms
    for n_i in range(0, _q_.num_atoms):
        atuple = atoms[n_i].atuple()
        _q_.atom_symbol.append(QMolecule.symbols[atuple[0]])
        _q_.atom_xyz[n_i][0] = atuple[1]
        _q_.atom_xyz[n_i][1] = atuple[2]
        _q_.atom_xyz[n_i][2] = atuple[3]
    # 1 and 2 electron integrals
    _q_.hcore = hij
    _q_.hcore_b = None
    _q_.kinetic = integrals.T
    _q_.overlap = integrals.S
    _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

    return _q_
def _calculate_integrals(molecule, basis='sto3g', hf_method='rhf', tol=1e-8, maxiters=100):
    """Function to calculate the one and two electron terms. Perform a Hartree-Fock calculation in
        the given basis.
    Args:
        molecule : A pyquante2 molecular object.
        basis : The basis set for the electronic structure computation
        hf_method: rhf, uhf, rohf
    Returns:
        QMolecule: QMolecule populated with driver integrals etc
    """
    bfs = basisset(molecule, basis)
    integrals = onee_integrals(bfs, molecule)
    hij = integrals.T + integrals.V
    hijkl = twoe_integrals(bfs)

    # convert overlap integrals to molecular basis
    # calculate the Hartree-Fock solution of the molecule

    if hf_method == 'rhf':
        solver = rhf(molecule, bfs)
    elif hf_method == 'rohf':
        solver = rohf(molecule, bfs)
    elif hf_method == 'uhf':
        solver = uhf(molecule, bfs)
    else:
        raise QiskitChemistryError('Invalid hf_method type: {}'.format(hf_method))
    ehf = solver.converge(tol=tol, maxiters=maxiters)
    logger.debug('PyQuante2 processing information:\n{}'.format(solver))
    if hasattr(solver, 'orbs'):
        orbs = solver.orbs
        orbs_B = None
    else:
        orbs = solver.orbsa
        orbs_B = solver.orbsb
    norbs = len(orbs)
    if hasattr(solver, 'orbe'):
        orbs_energy = solver.orbe
        orbs_energy_B = None
    else:
        orbs_energy = solver.orbea
        orbs_energy_B = solver.orbeb
    enuke = molecule.nuclear_repulsion()
    # Get ints in molecular orbital basis
    mohij = simx(hij, orbs)
    mohij_B = None
    if orbs_B is not None:
        mohij_B = simx(hij, orbs_B)

    eri = hijkl.transform(np.identity(norbs))
    mohijkl = hijkl.transform(orbs)
    mohijkl_BB = None
    mohijkl_BA = None
    if orbs_B is not None:
        mohijkl_BB = hijkl.transform(orbs_B)
        mohijkl_BA = np.einsum('aI,bJ,cK,dL,abcd->IJKL', orbs_B, orbs_B, orbs, orbs, hijkl[...])

    # Create driver level molecule object and populate
    _q_ = QMolecule()
    _q_.origin_driver_version = '?'  # No version info seems available to access
    # Energies and orbits
    _q_.hf_energy = ehf[0]
    _q_.nuclear_repulsion_energy = enuke
    _q_.num_orbitals = norbs
    _q_.num_alpha = molecule.nup()
    _q_.num_beta = molecule.ndown()
    _q_.mo_coeff = orbs
    _q_.mo_coeff_B = orbs_B
    _q_.orbital_energies = orbs_energy
    _q_.orbital_energies_B = orbs_energy_B
    # Molecule geometry
    _q_.molecular_charge = molecule.charge
    _q_.multiplicity = molecule.multiplicity
    _q_.num_atoms = len(molecule)
    _q_.atom_symbol = []
    _q_.atom_xyz = np.empty([len(molecule), 3])
    atoms = molecule.atoms
    for _n in range(0, _q_.num_atoms):
        atuple = atoms[_n].atuple()
        _q_.atom_symbol.append(QMolecule.symbols[atuple[0]])
        _q_.atom_xyz[_n][0] = atuple[1]
        _q_.atom_xyz[_n][1] = atuple[2]
        _q_.atom_xyz[_n][2] = atuple[3]
    # 1 and 2 electron integrals
    _q_.hcore = hij
    _q_.hcore_B = None
    _q_.kinetic = integrals.T
    _q_.overlap = integrals.S
    _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

    return _q_
Пример #11
0
def gvb(geo,npair=0,basisname='sto3g',maxiter=25,verbose=False,
        return_orbs=False, input_orbs=None):
    """\
    This is a trivial test for the gvb module, because other
    pyquante modules are simpler if you're doing closed shell rhf,
    and should give the same results.

    # -0.46658184546856041 from uhf/sto3g
    >>> gvb(h)     # doctest: +ELLIPSIS
    -0.4665818...

    #  -1.117099582955609 from rhf/sto3g
    >>> gvb(h2)    # doctest: +ELLIPSIS
    -1.117099...

    >>> gvb(lih,maxiter=5)   # doctest: +ELLIPSIS
    -7.86073...

    >>> gvb(li,maxiter=5)    # doctest: +ELLIPSIS
    -7.31552...

    >>> gvb(h2,npair=1)      # doctest: +ELLIPSIS
    -1.13730...
    """
    # Get the basis set and the integrals
    bfs = basisset(geo,basisname)
    i1 = onee_integrals(bfs,geo)
    i2 = twoe_integrals(bfs)
    h = i1.T + i1.V

    # Get a guess for the orbitals
    if input_orbs is not None:
        U = input_orbs
    else:
        E,U = geigh(h,i1.S)

    # Set the parameters based on the molecule
    nopen = geo.nopen()
    ncore = geo.nclosed() - npair
    nocc = ncore + nopen + 2*npair
    norb = len(bfs)
    virt = range(nocc,norb)
    orbs_per_shell = get_orbs_per_shell(ncore,nopen,npair)
    nsh = len(orbs_per_shell)
    shell = orbital_to_shell_mapping(ncore,nopen,npair)
    Enuke = geo.nuclear_repulsion()

    f,a,b = fab(ncore,nopen,npair)

    if verbose:
        np.set_printoptions(precision=4)
        print("**** PyQuante GVB ****")
        print(geo)
        print("Nuclear repulsion energy: %.3f" % Enuke)
        print("Basis set: %s" % basisname)
        print("  ncore/open/pair: %d,%d,%d" % (ncore,nopen,npair))
        print("  occ/bf/orb: %d,%d,%d" % (nocc,len(bfs),norb))
        for i in range(nsh):
            print("Shell %d" % i)
            print("  occupation = %.2f" % f[i])
            print("  orbitals in shell %s" % orbs_per_shell[i])
            print("  couplings to other shells %s" % zip(a[i,:],b[i,:]))
        print("Starting guess at orbitals:\n%s"%U)
        print("Shell array: %s" % shell)
        print("****")

    Eold = 0
    for it in range(maxiter):
        # Make all of the density matrices:
        Ds = [dmat_gen(U,orbs) for orbs in orbs_per_shell]
        # Compute the required Hamiltonian matrices:
        Js = [i2.get_j(D) for D in Ds]
        Ks = [i2.get_k(D) for D in Ds]

        # Perform the ROTION step and compute the energy
        Eel,Eone,Uocc = ROTION(U[:,:nocc],h,Js,Ks,f,a,b,nocc,shell,
                               verbose=verbose)
        if nsh > 1:
            U[:,:nocc] = Uocc

        # Perform the OCBSE step
        U = OCBSE(U,h,Js,Ks,f,a,b,orbs_per_shell,virt)

        #E = Enuke+Eone+Etwo
        E = Enuke+Eel
        Etwo = Eel-2*Eone

        # Update CI coefs
        coeffs = update_gvb_ci_coeffs(Uocc,h,Js,Ks,f,a,b,ncore,nopen,npair,
                                      orbs_per_shell,verbose)
        f,a,b = fab(ncore,nopen,npair,coeffs)

        if verbose:
            print ("---- %d :  %10.4f %10.4f %10.4f %10.4f" % ((it+1),E,Enuke,Eone,Etwo))
        if np.isclose(E,Eold):
            if verbose:
                print("Energy converged")
            break
        Eold = E
    else:
        print("Maximum iterations (%d) reached without convergence" % (maxiter))
    if return_orbs:
        return E,U
    return E
Пример #12
0
def gvb(geo,
        npair=0,
        basisname='sto3g',
        maxiter=25,
        verbose=False,
        return_orbs=False,
        input_orbs=None):
    """\
    This is a trivial test for the gvb module, because other
    pyquante modules are simpler if you're doing closed shell rhf,
    and should give the same results.

    # -0.46658184546856041 from uhf/sto3g
    >>> gvb(h)     # doctest: +ELLIPSIS
    -0.4665818...

    #  -1.117099582955609 from rhf/sto3g
    >>> gvb(h2)    # doctest: +ELLIPSIS
    -1.117099...

    >>> gvb(lih,maxiter=5)   # doctest: +ELLIPSIS
    -7.86073...

    >>> gvb(li,maxiter=5)    # doctest: +ELLIPSIS
    -7.31552...

    >>> gvb(h2,npair=1)      # doctest: +ELLIPSIS
    -1.13730...
    """
    # Get the basis set and the integrals
    bfs = basisset(geo, basisname)
    i1 = onee_integrals(bfs, geo)
    i2 = twoe_integrals(bfs)
    h = i1.T + i1.V

    # Get a guess for the orbitals
    if input_orbs is not None:
        U = input_orbs
    else:
        E, U = geigh(h, i1.S)

    # Set the parameters based on the molecule
    nopen = geo.nopen()
    ncore = geo.nclosed() - npair
    nocc = ncore + nopen + 2 * npair
    norb = len(bfs)
    virt = range(nocc, norb)
    orbs_per_shell = get_orbs_per_shell(ncore, nopen, npair)
    nsh = len(orbs_per_shell)
    shell = orbital_to_shell_mapping(ncore, nopen, npair)
    Enuke = geo.nuclear_repulsion()

    f, a, b = fab(ncore, nopen, npair)

    if verbose:
        np.set_printoptions(precision=4)
        print("**** PyQuante GVB ****")
        print(geo)
        print("Nuclear repulsion energy: %.3f" % Enuke)
        print("Basis set: %s" % basisname)
        print("  ncore/open/pair: %d,%d,%d" % (ncore, nopen, npair))
        print("  occ/bf/orb: %d,%d,%d" % (nocc, len(bfs), norb))
        for i in range(nsh):
            print("Shell %d" % i)
            print("  occupation = %.2f" % f[i])
            print("  orbitals in shell %s" % orbs_per_shell[i])
            print("  couplings to other shells %s" % zip(a[i, :], b[i, :]))
        print("Starting guess at orbitals:\n%s" % U)
        print("Shell array: %s" % shell)
        print("****")

    Eold = 0
    for it in range(maxiter):
        # Make all of the density matrices:
        Ds = [dmat_gen(U, orbs) for orbs in orbs_per_shell]
        # Compute the required Hamiltonian matrices:
        Js = [i2.get_j(D) for D in Ds]
        Ks = [i2.get_k(D) for D in Ds]

        # Perform the ROTION step and compute the energy
        Eel, Eone, Uocc = ROTION(U[:, :nocc],
                                 h,
                                 Js,
                                 Ks,
                                 f,
                                 a,
                                 b,
                                 nocc,
                                 shell,
                                 verbose=verbose)
        if nsh > 1:
            U[:, :nocc] = Uocc

        # Perform the OCBSE step
        U = OCBSE(U, h, Js, Ks, f, a, b, orbs_per_shell, virt)

        #E = Enuke+Eone+Etwo
        E = Enuke + Eel
        Etwo = Eel - 2 * Eone

        # Update CI coefs
        coeffs = update_gvb_ci_coeffs(Uocc, h, Js, Ks, f, a, b, ncore, nopen,
                                      npair, orbs_per_shell, verbose)
        f, a, b = fab(ncore, nopen, npair, coeffs)

        if verbose:
            print("---- %d :  %10.4f %10.4f %10.4f %10.4f" %
                  ((it + 1), E, Enuke, Eone, Etwo))
        if np.isclose(E, Eold):
            if verbose:
                print("Energy converged")
            break
        Eold = E
    else:
        print("Maximum iterations (%d) reached without convergence" %
              (maxiter))
    if return_orbs:
        return E, U
    return E