Exemplo n.º 1
0
def localizeValence(mf, mo_coeff, method="iao"):
    if (method == "iao"):
        return iao.iao(mf.mol, mo_coeff)
    elif (method == "ibo"):
        a = iao.iao(mf.mol, mo_coeff)
        a = lo.vec_lowdin(a, mf.get_ovlp())
        return ibo.ibo(mf.mol, mo_coeff, iaos=a)
    elif (method == "boys"):
        return boys.Boys(mf.mol).kernel(mo_coeff)
    elif (method == "er"):
        return edmiston.ER(mf.mol).kernel(mo_coeff)
Exemplo n.º 2
0
def localizeAllElectron(mf, method="lowdin"):
    if (method == "lowdin"):
        return fractional_matrix_power(mf.get_ovlp(), -0.5).T
    elif (method == "pm"):
        return pipek.PM(mf.mol).kernel(mf.mo_coeff)
    elif (method == "boys"):
        return boys.Boys(mf.mol).kernel(mf.mo_coeff)
    elif (method == "er"):
        return edmiston.ER(mf.mol).kernel(mf.mo_coeff)
    elif (method == "iao"):
        return iao.iao(mf.mol, mf.mo_coeff)
    elif (method == "ibo"):
        a = iao.iao(mf.mol, mf.mo_coeff)
        a = lo.vec_lowdin(a, mf.get_ovlp())
        return ibo.ibo(mf.mol, mf.mo_coeff, iaos=a)
Exemplo n.º 3
0
Arquivo: ibo.py Projeto: zzy2014/pyscf
def ibo(mol,
        orbocc,
        locmethod='IBO',
        iaos=None,
        s=None,
        exponent=4,
        grad_tol=1e-8,
        max_iter=200,
        verbose=logger.NOTE):
    '''Intrinsic Bonding Orbitals

    This function serves as a wrapper to the underlying localization functions
    ibo_loc and PipekMezey to create IBOs.

    Args:
        mol : the molecule or cell object

        orbocc : occupied molecular orbital coefficients

    Kwargs:
        locmethod : string
            the localization method 'PM' for Pipek Mezey localization or 'IBO' for the IBO localization

        iaos : 2D array
            the array of IAOs

        s : 2D array
            the overlap array in the ao basis

    Returns:
        IBOs in the basis defined in mol object.
    '''

    if s is None:
        if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
            if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
                s = mol.pbc_intor('int1e_ovlp', hermi=1)
            else:
                raise NotImplementedError('k-points crystal orbitals')
        else:
            s = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    locmethod = locmethod.strip().upper()
    if locmethod == 'PM':
        EXPONENT = getattr(__config__, 'lo_ibo_PipekMezey_exponent', exponent)
        ibos = PipekMezey(mol, orbocc, iaos, s, exponent=EXPONENT)
        del (EXPONENT)
    else:
        ibos = ibo_loc(mol,
                       orbocc,
                       iaos,
                       s,
                       exponent=exponent,
                       grad_tol=grad_tol,
                       max_iter=max_iter,
                       verbose=verbose)
    return ibos
Exemplo n.º 4
0
    def test_fast_iao_mulliken_pop(self):
        mf = scf.RHF(mol).run()
        a = iao.iao(mol, mf.mo_coeff[:, mf.mo_occ > 0])
        p, chg = iao.fast_iao_mullikan_pop(mol, mf.make_rdm1(), a)
        self.assertAlmostEqual(lib.finger(p), 0.56795867043723325, 5)

        mf = scf.UHF(mol).run()
        p, chg = iao.fast_iao_mullikan_pop(mol, mf.make_rdm1(), a)
        self.assertAlmostEqual(lib.finger(p[0] + p[1]), 0.56795867043723325, 5)
Exemplo n.º 5
0
    def test_fast_iao_mulliken_pop(self):
        mf = scf.RHF(mol).run()
        a = iao.iao(mol, mf.mo_coeff[:,mf.mo_occ>0])
        p,chg = iao.fast_iao_mullikan_pop(mol, mf.make_rdm1(), a)
        self.assertAlmostEqual(lib.finger(p), 0.56812564587009806, 5)

        mf = scf.UHF(mol).run()
        p,chg = iao.fast_iao_mullikan_pop(mol, mf.make_rdm1(), a)
        self.assertAlmostEqual(lib.finger(p[0]+p[1]), 0.56812564587009806, 5)
Exemplo n.º 6
0
def PipekMezey(mol, orbocc, iaos=None, s=None, exponent=EXPONENT):
    '''
    Note this localization is slightly different to Knizia's implementation.
    The localization here reserves orthogonormality during optimization.
    Orbitals are projected to IAO basis first and the Mulliken pop is
    calculated based on IAO basis (in function atomic_pops).  A series of
    unitary matrices are generated and applied on the input orbitals.  The
    intemdiate orbitals in the optimization and the finally localized orbitals
    are all orthogonormal.

    Examples:

    >>> from pyscf import gto, scf
    >>> from pyscf.lo import ibo
    >>> mol = gto.M(atom='H 0 0 0; F 0 0 1', >>> basis='unc-sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> pm = ibo.PM(mol, mf.mo_coeff[:,mf.mo_occ>0])
    >>> loc_orb = pm.kernel()
    '''
    if hasattr(mol, 'pbc_intor'):  # whether mol object is a cell
        if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
            s = mol.pbc_intor('int1e_ovlp', hermi=1)
        else:
            raise NotImplementedError('k-points crystal orbitals')
    else:
        s = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    # Different to Knizia's code, the reference IAOs are not neccessary
    # orthogonal.
    #iaos = orth.vec_lowdin(iaos, s)

    cs = numpy.dot(iaos.T.conj(), s)
    s_iao = numpy.dot(cs, iaos)
    iao_inv = numpy.linalg.solve(s_iao, cs)
    iao_mol = iao.reference_mol(mol)

    # Define the mulliken population of each atom based on IAO basis.
    # proj[i].trace is the mulliken population of atom i.
    def atomic_pops(mol, mo_coeff, method=None):
        nmo = mo_coeff.shape[1]
        proj = numpy.empty((mol.natm, nmo, nmo))
        orb_in_iao = reduce(numpy.dot, (iao_inv, mo_coeff))
        for i, (b0, b1, p0, p1) in enumerate(iao_mol.offset_nr_by_atom()):
            csc = reduce(numpy.dot,
                         (orb_in_iao[p0:p1].T, s_iao[p0:p1], orb_in_iao))
            proj[i] = (csc + csc.T) * .5
        return proj

    pm = pipek.PM(mol, orbocc)
    pm.atomic_pops = atomic_pops
    pm.exponent = exponent
    return pm
Exemplo n.º 7
0
def vvo(mol, orbocc, orbvirt, iaos=None, s=None, verbose=logger.NOTE):
    '''Valence Virtual Orbitals ref. 10.1021/acs.jctc.7b00493

    Valence virtual orbitals can be formed from the singular value 
    decomposition of the overlap between the canonical molecular orbitals
    and an accurate underlying atomic basis set. This implementation uses
    the intrinsic atomic orbital as this underlying set. VVOs can also be 
    formed from the null space of the overlap of the canonical molecular 
    orbitals and the underlying atomic basis sets (IAOs). This is not 
    implemented here.

    Args:
        mol : the molecule or cell object

        orbocc : occupied molecular orbital coefficients

        orbvirt : virtual molecular orbital coefficients

    Kwargs:
        iaos : 2D array
            the array of IAOs

        s : 2D array
            the overlap array in the ao basis

    Returns:
        VVOs in the basis defined in mol object.
    '''
    log = logger.new_logger(mol, verbose)
    if s is None:
        if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
            if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
                s = mol.pbc_intor('int1e_ovlp', hermi=1)
            else:
                raise NotImplementedError('k-points crystal orbitals')
        else:
            s = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    nvvo = iaos.shape[1] - orbocc.shape[1]

    # Symmetrically orthogonalization of the IAO orbitals as Knizia's
    # implementation.  The IAO returned by iao.iao function is not orthogonal.
    iaos = orth.vec_lowdin(iaos, s)

    #S = reduce(np.dot, (orbvirt.T, s, iaos))
    S = numpy.einsum('ji,jk,kl->il', orbvirt, s, iaos, optimize=True)
    U, sigma, Vh = scipy.linalg.svd(S)
    U = U[:, 0:nvvo]
    vvo = numpy.einsum('ik,ji->jk', U, orbvirt, optimize=True)
    return vvo
Exemplo n.º 8
0
def PipekMezey(mol, orbocc, iaos=None, s=None, exponent=EXPONENT):
    '''
    Note this localization is slightly different to Knizia's implementation.
    The localization here reserves orthogonormality during optimization.
    Orbitals are projected to IAO basis first and the Mulliken pop is
    calculated based on IAO basis (in function atomic_pops).  A series of
    unitary matrices are generated and applied on the input orbitals.  The
    intemdiate orbitals in the optimization and the finally localized orbitals
    are all orthogonormal.

    Examples:

    >>> from pyscf import gto, scf
    >>> from pyscf.lo import ibo
    >>> mol = gto.M(atom='H 0 0 0; F 0 0 1', >>> basis='unc-sto3g')
    >>> mf = scf.RHF(mol).run()
    >>> pm = ibo.PM(mol, mf.mo_coeff[:,mf.mo_occ>0])
    >>> loc_orb = pm.kernel()
    '''
    if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
        if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
            s = mol.pbc_intor('int1e_ovlp', hermi=1)
        else:
            raise NotImplementedError('k-points crystal orbitals')
    else:
        s = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    # Different to Knizia's code, the reference IAOs are not neccessary
    # orthogonal.
    #iaos = orth.vec_lowdin(iaos, s)

    cs = numpy.dot(iaos.T.conj(), s)
    s_iao = numpy.dot(cs, iaos)
    iao_inv = numpy.linalg.solve(s_iao, cs)
    iao_mol = iao.reference_mol(mol)
    # Define the mulliken population of each atom based on IAO basis.
    # proj[i].trace is the mulliken population of atom i.
    def atomic_pops(mol, mo_coeff, method=None):
        nmo = mo_coeff.shape[1]
        proj = numpy.empty((mol.natm,nmo,nmo))
        orb_in_iao = reduce(numpy.dot, (iao_inv, mo_coeff))
        for i, (b0, b1, p0, p1) in enumerate(iao_mol.offset_nr_by_atom()):
            csc = reduce(numpy.dot, (orb_in_iao[p0:p1].T, s_iao[p0:p1],
                                     orb_in_iao))
            proj[i] = (csc + csc.T) * .5
        return proj
    pm = pipek.PM(mol, orbocc)
    pm.atomic_pops = atomic_pops
    pm.exponent = exponent
    return pm
Exemplo n.º 9
0
def kernel(mf,
           aolabels,
           threshold=THRESHOLD,
           minao=MINAO,
           with_iao=WITH_IAO,
           openshell_option=OPENSHELL_OPTION,
           canonicalize=CANONICALIZE,
           ncore=0,
           verbose=None):
    '''AVAS method to construct mcscf active space.
    Ref. arXiv:1701.07862 [physics.chem-ph]

    Args:
        mf : an :class:`SCF` object

        aolabels : string or a list of strings
            AO labels for AO active space

    Kwargs:
        threshold : float
            Tructing threshold of the AO-projector above which AOs are kept in
            the active space.
        minao : str
            A reference AOs for AVAS.
        with_iao : bool
            Whether to use IAO localization to construct the reference active AOs.
        openshell_option : int
            How to handle singly-occupied orbitals in the active space. The
            singly-occupied orbitals are projected as part of alpha orbitals
            if openshell_option=2, or completely kept in active space if
            openshell_option=3.  See Section III.E option 2 or 3 of the
            reference paper for more details.
        canonicalize : bool
            Orbitals defined in AVAS method are local orbitals.  Symmetrizing
            the core, active and virtual space.
        ncore : integer
            Number of core orbitals to exclude from the AVAS method.

    Returns:
        active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF

    Examples:

    >>> from pyscf import gto, scf, mcscf
    >>> from pyscf.mcscf import avas
    >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz')
    >>> mf = scf.RHF(mol).run()
    >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s'])
    >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo)
    '''
    from pyscf.tools import mo_mapping

    if isinstance(verbose, logger.Logger):
        log = verbose
    elif verbose is not None:
        log = logger.Logger(mf.stdout, verbose)
    else:
        log = logger.Logger(mf.stdout, mf.verbose)
    mol = mf.mol

    log.info('\n** AVAS **')
    if isinstance(mf, scf.uhf.UHF):
        log.note('UHF/UKS object is found.  AVAS takes alpha orbitals only')
        mo_coeff = mf.mo_coeff[0]
        mo_occ = mf.mo_occ[0]
        mo_energy = mf.mo_energy[0]
        assert (openshell_option != 1)
    else:
        mo_coeff = mf.mo_coeff
        mo_occ = mf.mo_occ
        mo_energy = mf.mo_energy
    nocc = numpy.count_nonzero(mo_occ != 0)
    ovlp = mol.intor_symmetric('int1e_ovlp')
    log.info('  Total number of HF MOs  is equal to    %d', mo_coeff.shape[1])
    log.info('  Number of occupied HF MOs is equal to  %d', nocc)

    mol = mf.mol
    pmol = mol.copy()
    pmol.atom = mol._atom
    pmol.unit = 'B'
    pmol.symmetry = False
    pmol.basis = minao
    pmol.build(False, False)

    baslst = pmol.search_ao_label(aolabels)
    log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst)

    if with_iao:
        from pyscf.lo import iao
        c = iao.iao(mol, mo_coeff[:, ncore:nocc], minao)[:, baslst]
        s2 = reduce(numpy.dot, (c.T, ovlp, c))
        s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:]))
    else:
        s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:, baslst]
        s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst]
        s21 = numpy.dot(s21, mo_coeff[:, ncore:])
    sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True))

    from pyscf.symm import label_orb_symm
    symm = label_orb_symm(mol,
                          mol.irrep_name,
                          mol.symm_orb,
                          mo_coeff,
                          tol=1e-5)

    if openshell_option == 2:
        wocc, u = numpy.linalg.eigh(sa[:(nocc - ncore), :(nocc - ncore)])
        log.info('Option 2: threshold %s', threshold)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        if ncore > 0: mofreeze = mo_coeff[:, :ncore]
        mocore = mo_coeff[:, ncore:nocc].dot(u[:, wocc < threshold])
        mocas = mo_coeff[:, ncore:nocc].dot(u[:, wocc > threshold])
        mask = (wocc > threshold).tolist()
        #print nocc-ncore,mocore.shape,wocc<threshold
        #print nocc-ncore,mocas.shape,wocc>threshold
        #print 'BM',mask,len(mask)

        wvir, u = numpy.linalg.eigh(sa[(nocc - ncore):, (nocc - ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack(
            (mocas, mo_coeff[:, nocc:].dot(u[:, wvir > threshold])))
        movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold])
        ncas = mocas.shape[1]
        mask += (wvir > threshold).tolist()
        #print mo_coeff.shape[0]-nocc,mocas.shape,wvir>threshold
        #print mo_coeff.shape[0]-nocc,movir.shape,wvir<threshold
        #print 'BM',mask,len(mask)

    elif openshell_option == 3:
        docc = nocc - mol.spin
        wocc, u = numpy.linalg.eigh(sa[:(docc - ncore), :(docc - ncore)])
        log.info('Option 3: threshold %s, num open shell %d', threshold,
                 mol.spin)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        if ncore > 0: mofreeze = mo_coeff[:, :ncore]
        mocore = mo_coeff[:, ncore:docc].dot(u[:, wocc < threshold])
        mocas = mo_coeff[:, ncore:docc].dot(u[:, wocc > threshold])

        wvir, u = numpy.linalg.eigh(sa[(nocc - ncore):, (nocc - ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas, mo_coeff[:, docc:nocc],
                              mo_coeff[:, nocc:].dot(u[:, wvir > threshold])))
        movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold])
        ncas = mocas.shape[1]

    log.debug('projected occ eig %s', wocc[::-1])
    log.debug('projected vir eig %s', wvir[::-1])
    log.info('Active from occupied = %d , eig %s', ncas_occ,
             wocc[wocc > threshold][::-1])
    log.info('Inactive from occupied = %d', mocore.shape[1])
    log.info('Active from unoccupied = %d , eig %s', ncas_vir,
             wvir[wvir > threshold][::-1])
    log.info('Inactive from unoccupied = %d', movir.shape[1])
    log.info('Dimensions of active %d', ncas)
    nalpha = (nelecas + mol.spin) // 2
    nbeta = nelecas - nalpha
    log.info('# of alpha electrons %d', nalpha)
    log.info('# of beta electrons %d', nbeta)
    selected = [i for i in range(len(mask)) if mask[i]]
    list_of_sym = [symm[i] for i in selected]
    print selected
    print list_of_sym
    for elt in set(symm):
        n = 0
        for i in list_of_sym:
            if i == elt: n += 1
        print elt, n

    if canonicalize:
        from pyscf.mcscf import dmet_cas

        def trans(c):
            if c.shape[1] == 0:
                return c
            else:
                csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff))
                fock = numpy.dot(csc * mo_energy, csc.T)
                e, u = scipy.linalg.eigh(fock)
                return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log)

        if ncore > 0:
            mo = numpy.hstack(
                [trans(mofreeze),
                 trans(mocore),
                 trans(mocas),
                 trans(movir)])
        else:
            mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)])
    else:
        mo = numpy.hstack((mocore, mocas, movir))
    return ncas, nelecas, mo
Exemplo n.º 10
0
def atomic_pops(mol, mo_coeff, method='meta_lowdin', mf=None):
    '''
    Kwargs:
        method : string
            The atomic population projection scheme. It can be mulliken,
            lowdin, meta_lowdin, iao, or becke

    Returns:
        A 3-index tensor [A,i,j] indicates the population of any orbital-pair
        density |i><j| for each species (atom in this case).  This tensor is
        used to construct the population and gradients etc.

        You can customize the PM localization wrt other population metric,
        such as the charge of a site, the charge of a fragment (a group of
        atoms) by overwriting this tensor.  See also the example
        pyscf/examples/loc_orb/40-hubbard_model_PM_localization.py for the PM
        localization of site-based population for hubbard model.
    '''
    method = method.lower().replace('_', '-')
    nmo = mo_coeff.shape[1]
    proj = numpy.empty((mol.natm,nmo,nmo))

    if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
        s = mol.pbc_intor('int1e_ovlp_sph', hermi=1)
    else:
        s = mol.intor_symmetric('int1e_ovlp')

    if method == 'becke':
        from pyscf.dft import gen_grid
        if not (getattr(mf, 'grids', None) and getattr(mf, '_numint', None)):
            # Call DFT to initialize grids and numint objects
            mf = mol.RKS()
        grids = mf.grids
        ni = mf._numint

        if not isinstance(grids, gen_grid.Grids):
            raise NotImplementedError('PM becke scheme for PBC systems')

        # The atom-wise Becke grids (without concatenated to a vector of grids)
        coords, weights = grids.get_partition(mol, concat=False)

        for i in range(mol.natm):
            ao = ni.eval_ao(mol, coords[i], deriv=0)
            aow = numpy.einsum('pi,p->pi', ao, weights[i])
            charge_matrix = lib.dot(aow.conj().T, ao)
            proj[i] = reduce(lib.dot, (mo_coeff.conj().T, charge_matrix, mo_coeff))

    elif method == 'mulliken':
        for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()):
            csc = reduce(numpy.dot, (mo_coeff[p0:p1].conj().T, s[p0:p1], mo_coeff))
            proj[i] = (csc + csc.conj().T) * .5

    elif method in ('lowdin', 'meta-lowdin'):
        csc = reduce(lib.dot, (mo_coeff.conj().T, s, orth.orth_ao(mol, method, 'ANO', s=s)))
        for i, (b0, b1, p0, p1) in enumerate(mol.offset_nr_by_atom()):
            proj[i] = numpy.dot(csc[:,p0:p1], csc[:,p0:p1].conj().T)

    elif method in ('iao', 'ibo'):
        from pyscf.lo import iao
        assert mf is not None
        # FIXME: How to handle UHF/UKS object?
        orb_occ = mf.mo_coeff[:,mf.mo_occ>0]

        iao_coeff = iao.iao(mol, orb_occ)
        #
        # IAO is generally not orthogonalized. For simplicity, we take Lowdin
        # orthogonalization here. Other orthogonalization can be used. Results
        # should be very closed to the Lowdin-orth orbitals
        #
        # PM with Mulliken population of non-orth IAOs can be found in
        # ibo.PipekMezey function
        #
        iao_coeff = orth.vec_lowdin(iao_coeff, s)
        csc = reduce(lib.dot, (mo_coeff.conj().T, s, iao_coeff))

        iao_mol = iao.reference_mol(mol)
        for i, (b0, b1, p0, p1) in enumerate(iao_mol.offset_nr_by_atom()):
            proj[i] = numpy.dot(csc[:,p0:p1], csc[:,p0:p1].conj().T)

    else:
        raise KeyError('method = %s' % method)

    return proj
Exemplo n.º 11
0
mol.basis = 'aug-cc-pvtz'
mol.symmetry = 1
mol.build()

mf = dft.RKS(mol)
mf.xc = 'HF*0.2 + .08*LDA + .72*B88, .81*LYP + .19*VWN5'
mf.kernel()

orbocc = mf.mo_coeff[:,0:mol.nelec[0]]
orbvirt = mf.mo_coeff[:,mol.nelec[0]:]
mocoeff = mf.mo_coeff

ovlpS = mol.intor_symmetric('int1e_ovlp')

# plot canonical mos
iaos = iao.iao(mol, orbocc)
iaos = orth.vec_lowdin(iaos, ovlpS)
for i in range(iaos.shape[1]):
    tools.cubegen.orbital(mol, 'h2o_cmo_{:02d}.cube'.format(i+1), mocoeff[:,i])

# plot intrinsic atomic orbitals
for i in range(iaos.shape[1]):
    tools.cubegen.orbital(mol, 'h2o_iao_{:02d}.cube'.format(i+1), iaos[:,i])

# plot intrinsic bonding orbitals
count = 0
ibos = lo.ibo.ibo(mol, orbocc, locmethod='IBO')
for i in range(ibos.shape[1]):
    count += 1
    tools.cubegen.orbital(mol, 'h2o_ibo_{:02d}.cube'.format(count), ibos[:,i])
Exemplo n.º 12
0
def kernel(mf, aolabels, threshold=THRESHOLD, minao=MINAO, with_iao=WITH_IAO,
           openshell_option=OPENSHELL_OPTION, canonicalize=CANONICALIZE,
           ncore=0, verbose=None):
    '''AVAS method to construct mcscf active space.
    Ref. arXiv:1701.07862 [physics.chem-ph]

    Args:
        mf : an :class:`SCF` object

        aolabels : string or a list of strings
            AO labels for AO active space

    Kwargs:
        threshold : float
            Tructing threshold of the AO-projector above which AOs are kept in
            the active space.
        minao : str
            A reference AOs for AVAS.
        with_iao : bool
            Whether to use IAO localization to construct the reference active AOs.
        openshell_option : int
            How to handle singly-occupied orbitals in the active space. The
            singly-occupied orbitals are projected as part of alpha orbitals
            if openshell_option=2, or completely kept in active space if
            openshell_option=3.  See Section III.E option 2 or 3 of the
            reference paper for more details.
        canonicalize : bool
            Orbitals defined in AVAS method are local orbitals.  Symmetrizing
            the core, active and virtual space.
        ncore : integer
            Number of core orbitals to exclude from the AVAS method.

    Returns:
        active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF

    Examples:

    >>> from pyscf import gto, scf, mcscf
    >>> from pyscf.mcscf import avas
    >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz')
    >>> mf = scf.RHF(mol).run()
    >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s'])
    >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo)
    '''
    from pyscf.tools import mo_mapping

    if isinstance(verbose, logger.Logger):
        log = verbose
    elif verbose is not None:
        log = logger.Logger(mf.stdout, verbose)
    else:
        log = logger.Logger(mf.stdout, mf.verbose)
    mol = mf.mol

    log.info('\n** AVAS **')
    if isinstance(mf, scf.uhf.UHF):
        log.note('UHF/UKS object is found.  AVAS takes alpha orbitals only')
        mo_coeff = mf.mo_coeff[0]
        mo_occ = mf.mo_occ[0]
        mo_energy = mf.mo_energy[0]
        assert(openshell_option != 1)
    else:
        mo_coeff = mf.mo_coeff
        mo_occ = mf.mo_occ
        mo_energy = mf.mo_energy
    nocc = numpy.count_nonzero(mo_occ != 0)
    ovlp = mol.intor_symmetric('int1e_ovlp')
    log.info('  Total number of HF MOs  is equal to    %d' ,mo_coeff.shape[1])
    log.info('  Number of occupied HF MOs is equal to  %d', nocc)

    mol = mf.mol
    pmol = mol.copy()
    pmol.atom = mol._atom
    pmol.unit = 'B'
    pmol.symmetry = False
    pmol.basis = minao
    pmol.build(False, False)

    baslst = pmol.search_ao_label(aolabels)
    log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst)

    if with_iao:
        from pyscf.lo import iao
        c = iao.iao(mol, mo_coeff[:,ncore:nocc], minao)[:,baslst]
        s2 = reduce(numpy.dot, (c.T, ovlp, c))
        s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:]))
    else:
        s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:,baslst]
        s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst]
        s21 = numpy.dot(s21, mo_coeff[:, ncore:])
    sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True))

    if openshell_option == 2:
        wocc, u = numpy.linalg.eigh(sa[:(nocc-ncore), :(nocc-ncore)])
        log.info('Option 2: threshold %s', threshold)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        if ncore > 0: mofreeze = mo_coeff[:,:ncore]
        mocore = mo_coeff[:,ncore:nocc].dot(u[:,wocc<threshold])
        mocas = mo_coeff[:,ncore:nocc].dot(u[:,wocc>threshold])

        wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas, mo_coeff[:,nocc:].dot(u[:,wvir>threshold])))
        movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold])
        ncas = mocas.shape[1]

    elif openshell_option == 3:
        docc = nocc - mol.spin
        wocc, u = numpy.linalg.eigh(sa[:(docc-ncore),:(docc-ncore)])
        log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        if ncore > 0: mofreeze = mo_coeff[:,:ncore]
        mocore = mo_coeff[:,ncore:docc].dot(u[:,wocc<threshold])
        mocas = mo_coeff[:,ncore:docc].dot(u[:,wocc>threshold])

        wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas, mo_coeff[:,docc:nocc],
                              mo_coeff[:,nocc:].dot(u[:,wvir>threshold])))
        movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold])
        ncas = mocas.shape[1]

    log.debug('projected occ eig %s', wocc[::-1])
    log.debug('projected vir eig %s', wvir[::-1])
    log.info('Active from occupied = %d , eig %s', ncas_occ, wocc[wocc>threshold][::-1])
    log.info('Inactive from occupied = %d', mocore.shape[1])
    log.info('Active from unoccupied = %d , eig %s', ncas_vir, wvir[wvir>threshold][::-1])
    log.info('Inactive from unoccupied = %d', movir.shape[1])
    log.info('Dimensions of active %d', ncas)
    nalpha = (nelecas + mol.spin) // 2
    nbeta = nelecas - nalpha
    log.info('# of alpha electrons %d', nalpha)
    log.info('# of beta electrons %d', nbeta)

    if canonicalize:
        from pyscf.mcscf import dmet_cas
        def trans(c):
            if c.shape[1] == 0:
                return c
            else:
                csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff))
                fock = numpy.dot(csc*mo_energy, csc.T)
                e, u = scipy.linalg.eigh(fock)
                return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log)
        if ncore > 0:
           mo = numpy.hstack([trans(mofreeze), trans(mocore), trans(mocas), trans(movir)])
        else:
           mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)])
    else:
        mo = numpy.hstack((mocore, mocas, movir))
    return ncas, nelecas, mo
Exemplo n.º 13
0
def livvo(mol,
          orbocc,
          orbvirt,
          locmethod='IBO',
          iaos=None,
          s=None,
          exponent=4,
          grad_tol=1e-8,
          max_iter=200,
          verbose=logger.NOTE):
    '''Localized Intrinsic Valence Virtual Orbitals ref. 10.1021/acs.jctc.7b00493

    Localized Intrinsic valence virtual orbitals are formed when the valence
    virtual orbitals are localized using an IBO-type of localization. Here
    the VVOs are created in the IAO basis then the IBO localization functions
    are called to localize the VVOs.

    Args:
        mol : the molecule or cell object

        orbocc : occupied molecular orbital coefficients

        orbvirt : virtual molecular orbital coefficients

    Kwargs:
        locmethod : string
            the localization method 'PM' for Pipek Mezey localization or 'IBO' for the IBO localization

        iaos : 2D array
            the array of IAOs

        s : 2D array
            the overlap array in the ao basis

    Returns:
        LIVVOs in the basis defined in mol object.
    '''
    if s is None:
        if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
            if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
                s = mol.pbc_intor('int1e_ovlp', hermi=1)
            else:
                raise NotImplementedError('k-points crystal orbitals')
        else:
            s = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    vvos = vvo(mol, orbocc, orbvirt, iaos=iaos, s=s)
    locmethod = locmethod.strip().upper()
    if locmethod == 'PM':
        EXPONENT = getattr(__config__, 'lo_ibo_PipekMezey_exponent', exponent)
        livvos = ibo.PipekMezey(mol, vvos, iaos, s, exponent=EXPONENT)
        del (EXPONENT)
    else:
        livvos = ibo.ibo_loc(mol,
                             vvos,
                             iaos,
                             s,
                             exponent=exponent,
                             grad_tol=grad_tol,
                             max_iter=max_iter,
                             verbose=verbose)
    return livvos
Exemplo n.º 14
0
def ibo(mol,
        orbocc,
        iaos=None,
        exponent=4,
        grad_tol=1e-8,
        max_iter=200,
        verbose=logger.NOTE):
    '''Intrinsic Bonding Orbitals. [Ref. JCTC, 9, 4834]

    This implementation follows Knizia's implementation execept that the
    resultant IBOs are symmetrically orthogonalized.  Note the IBOs of this
    implementation do not strictly maximize the IAO Mulliken charges.

    IBOs can also be generated by another implementation (see function
    pyscf.lo.ibo.PM). In that function, PySCF builtin Pipek-Mezey localization
    module was used to maximize the IAO Mulliken charges.

    Args:
        mol : the molecule or cell object

        orbocc : 2D array or a list of 2D array
            occupied molecular orbitals or crystal orbitals for each k-point

    Kwargs:
        iaos : 2D array
            the array of IAOs
        exponent : integer
            Localization power in PM scheme
        grad_tol : float
            convergence tolerance for norm of gradients

    Returns:
        IBOs in the big basis (the basis defined in mol object).
    '''
    log = logger.new_logger(mol, verbose)
    assert (exponent in (2, 4))

    if hasattr(mol, 'pbc_intor'):  # whether mol object is a cell
        if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
            ovlpS = mol.pbc_intor('int1e_ovlp', hermi=1)
        else:
            raise NotImplementedError('k-points crystal orbitals')
    else:
        ovlpS = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    # Symmetrically orthogonalization of the IAO orbitals as Knizia's
    # implementation.  The IAO returned by iao.iao function is not orthogonal.
    iaos = orth.vec_lowdin(iaos, ovlpS)

    #static variables
    StartTime = time()
    L = 0  # initialize a value of the localization function for safety
    #max_iter = 20000 #for some reason the convergence of solid is slower
    #fGradConv = 1e-10 #this ought to be pumped up to about 1e-8 but for testing purposes it's fine
    swapGradTolerance = 1e-12

    #dynamic variables
    Converged = False

    Atoms = [mol.atom_symbol(i) for i in range(mol.natm)]

    #generates the parameters we need about the atomic structure
    nAtoms = len(Atoms)
    AtomOffsets = MakeAtomIbOffsets(Atoms)[0]
    iAtSl = [slice(AtomOffsets[A], AtomOffsets[A + 1]) for A in range(nAtoms)]
    #converts the occupied MOs to the IAO basis
    CIb = reduce(numpy.dot, (iaos.T, ovlpS, orbocc))
    numOccOrbitals = CIb.shape[1]

    log.debug("   {0:^5s} {1:^14s} {2:^11s} {3:^8s}".format(
        "ITER.", "LOC(Orbital)", "GRADIENT", "TIME"))

    for it in range(max_iter):
        fGrad = 0.00

        #calculate L for convergence checking
        L = 0.
        for A in range(nAtoms):
            for i in range(numOccOrbitals):
                CAi = CIb[iAtSl[A], i]
                L += numpy.dot(CAi, CAi)**exponent

        # loop over the occupied orbitals pairs i,j
        for i in range(numOccOrbitals):
            for j in range(i):
                # I eperimented with exponentially falling off random noise
                Aij = 0.0  #numpy.random.random() * numpy.exp(-1*it)
                Bij = 0.0  #numpy.random.random() * numpy.exp(-1*it)
                for k in range(nAtoms):
                    CIbA = CIb[iAtSl[k], :]
                    Cii = numpy.dot(CIbA[:, i], CIbA[:, i])
                    Cij = numpy.dot(CIbA[:, i], CIbA[:, j])
                    Cjj = numpy.dot(CIbA[:, j], CIbA[:, j])
                    #now I calculate Aij and Bij for the gradient search
                    if exponent == 2:
                        Aij += 4. * Cij**2 - (Cii - Cjj)**2
                        Bij += 4. * Cij * (Cii - Cjj)
                    else:
                        Bij += 4. * Cij * (Cii**3 - Cjj**3)
                        Aij += -Cii**4 - Cjj**4 + 6 * (
                            Cii**2 +
                            Cjj**2) * Cij**2 + Cii**3 * Cjj + Cii * Cjj**3

                if (Aij**2 + Bij**2 < swapGradTolerance) and False:
                    continue
                    #this saves us from replacing already fine orbitals
                else:
                    #THE BELOW IS TAKEN DIRECLTY FROMG KNIZIA's FREE CODE
                    # Calculate 2x2 rotation angle phi.
                    # This correspond to [2] (12)-(15), re-arranged and simplified.
                    phi = .25 * numpy.arctan2(Bij, -Aij)
                    fGrad += Bij**2
                    # ^- Bij is the actual gradient. Aij is effectively
                    #    the second derivative at phi=0.

                    # 2x2 rotation form; that's what PM suggest. it works
                    # fine, but I don't like the asymmetry.
                    cs = numpy.cos(phi)
                    ss = numpy.sin(phi)
                    Ci = 1. * CIb[:, i]
                    Cj = 1. * CIb[:, j]
                    CIb[:, i] = cs * Ci + ss * Cj
                    CIb[:, j] = -ss * Ci + cs * Cj
        fGrad = fGrad**.5

        log.debug(" {0:5d} {1:12.8f} {2:11.2e} {3:8.2f}".format(
            it + 1, L**(1. / exponent), fGrad,
            time() - StartTime))
        if fGrad < grad_tol:
            Converged = True
            break
    Note = "IB/P%i/2x2, %i iter; Final gradient %.2e" % (exponent, it + 1,
                                                         fGrad)
    if not Converged:
        log.note(
            "\nWARNING: Iterative localization failed to converge!"
            "\n         %s", Note)
    else:
        log.note(" Iterative localization: %s", Note)
    log.debug(
        " Localized orbitals deviation from orthogonality: %8.2e",
        numpy.linalg.norm(numpy.dot(CIb.T, CIb) - numpy.eye(numOccOrbitals)))
    # Note CIb is not unitary matrix (although very close to unitary matrix)
    # because the projection <IAO|OccOrb> does not give unitary matrix.
    return numpy.dot(iaos, (orth.vec_lowdin(CIb)))
Exemplo n.º 15
0
def ibo(mol, orbocc, iaos=None, exponent=4, grad_tol=1e-8, max_iter=200,
        verbose=logger.NOTE):
    '''Intrinsic Bonding Orbitals. [Ref. JCTC, 9, 4834]

    This implementation follows Knizia's implementation execept that the
    resultant IBOs are symmetrically orthogonalized.  Note the IBOs of this
    implementation do not strictly maximize the IAO Mulliken charges.

    IBOs can also be generated by another implementation (see function
    pyscf.lo.ibo.PM). In that function, PySCF builtin Pipek-Mezey localization
    module was used to maximize the IAO Mulliken charges.

    Args:
        mol : the molecule or cell object

        orbocc : 2D array or a list of 2D array
            occupied molecular orbitals or crystal orbitals for each k-point

    Kwargs:
        iaos : 2D array
            the array of IAOs
        exponent : integer
            Localization power in PM scheme
        grad_tol : float
            convergence tolerance for norm of gradients

    Returns:
        IBOs in the big basis (the basis defined in mol object).
    '''
    log = logger.new_logger(mol, verbose)
    assert(exponent in (2, 4))

    if getattr(mol, 'pbc_intor', None):  # whether mol object is a cell
        if isinstance(orbocc, numpy.ndarray) and orbocc.ndim == 2:
            ovlpS = mol.pbc_intor('int1e_ovlp', hermi=1)
        else:
            raise NotImplementedError('k-points crystal orbitals')
    else:
        ovlpS = mol.intor_symmetric('int1e_ovlp')

    if iaos is None:
        iaos = iao.iao(mol, orbocc)

    # Symmetrically orthogonalization of the IAO orbitals as Knizia's
    # implementation.  The IAO returned by iao.iao function is not orthogonal.
    iaos = orth.vec_lowdin(iaos, ovlpS)

    #static variables
    StartTime = time()
    L  = 0 # initialize a value of the localization function for safety
    #max_iter = 20000 #for some reason the convergence of solid is slower
    #fGradConv = 1e-10 #this ought to be pumped up to about 1e-8 but for testing purposes it's fine
    swapGradTolerance = 1e-12

    #dynamic variables
    Converged = False

    Atoms  = [mol.atom_symbol(i) for i in range(mol.natm)]

    #generates the parameters we need about the atomic structure
    nAtoms = len(Atoms)
    AtomOffsets = MakeAtomIbOffsets(Atoms)[0]
    iAtSl = [slice(AtomOffsets[A],AtomOffsets[A+1]) for A in range(nAtoms)]
    #converts the occupied MOs to the IAO basis
    CIb = reduce(numpy.dot, (iaos.T, ovlpS , orbocc))
    numOccOrbitals = CIb.shape[1]

    log.debug("   {0:^5s} {1:^14s} {2:^11s} {3:^8s}"
              .format("ITER.","LOC(Orbital)","GRADIENT", "TIME"))

    for it in range(max_iter):
        fGrad = 0.00

        #calculate L for convergence checking
        L = 0.
        for A in range(nAtoms):
            for i in range(numOccOrbitals):
                CAi = CIb[iAtSl[A],i]
                L += numpy.dot(CAi,CAi)**exponent

        # loop over the occupied orbitals pairs i,j
        for i in range(numOccOrbitals):
            for j in range(i):
                # I eperimented with exponentially falling off random noise
                Aij  = 0.0 #numpy.random.random() * numpy.exp(-1*it)
                Bij  = 0.0 #numpy.random.random() * numpy.exp(-1*it)
                for k in range(nAtoms):
                    CIbA = CIb[iAtSl[k],:]
                    Cii  = numpy.dot(CIbA[:,i], CIbA[:,i])
                    Cij  = numpy.dot(CIbA[:,i], CIbA[:,j])
                    Cjj  = numpy.dot(CIbA[:,j], CIbA[:,j])
                    #now I calculate Aij and Bij for the gradient search
                    if exponent == 2:
                        Aij += 4.*Cij**2 - (Cii - Cjj)**2
                        Bij += 4.*Cij*(Cii - Cjj)
                    else:
                        Bij += 4.*Cij*(Cii**3-Cjj**3)
                        Aij += -Cii**4 - Cjj**4 + 6*(Cii**2 + Cjj**2)*Cij**2 + Cii**3 * Cjj + Cii*Cjj**3

                if (Aij**2 + Bij**2 < swapGradTolerance) and False:
                    continue
                    #this saves us from replacing already fine orbitals
                else:
                    #THE BELOW IS TAKEN DIRECLTY FROMG KNIZIA's FREE CODE
                    # Calculate 2x2 rotation angle phi.
                    # This correspond to [2] (12)-(15), re-arranged and simplified.
                    phi = .25*numpy.arctan2(Bij,-Aij)
                    fGrad += Bij**2
                    # ^- Bij is the actual gradient. Aij is effectively
                    #    the second derivative at phi=0.

                    # 2x2 rotation form; that's what PM suggest. it works
                    # fine, but I don't like the asymmetry.
                    cs = numpy.cos(phi)
                    ss = numpy.sin(phi)
                    Ci = 1. * CIb[:,i]
                    Cj = 1. * CIb[:,j]
                    CIb[:,i] =  cs * Ci + ss * Cj
                    CIb[:,j] = -ss * Ci + cs * Cj
        fGrad = fGrad**.5

        log.debug(" {0:5d} {1:12.8f} {2:11.2e} {3:8.2f}"
                  .format(it+1, L**(1./exponent), fGrad, time()-StartTime))
        if fGrad < grad_tol:
            Converged = True
            break
    Note = "IB/P%i/2x2, %i iter; Final gradient %.2e" % (exponent, it+1, fGrad)
    if not Converged:
        log.note("\nWARNING: Iterative localization failed to converge!"
                 "\n         %s", Note)
    else:
        log.note(" Iterative localization: %s", Note)
    log.debug(" Localized orbitals deviation from orthogonality: %8.2e",
              numpy.linalg.norm(numpy.dot(CIb.T, CIb) - numpy.eye(numOccOrbitals)))
    # Note CIb is not unitary matrix (although very close to unitary matrix)
    # because the projection <IAO|OccOrb> does not give unitary matrix.
    return numpy.dot(iaos, (orth.vec_lowdin(CIb)))
Exemplo n.º 16
0
def kernel(mf,
           aolabels,
           threshold=.2,
           minao='minao',
           with_iao=False,
           openshelloption=2,
           canonicalize=True,
           verbose=None):
    '''AVAS method to construct mcscf active space.
    Ref. arXiv:1701.07862 [physics.chem-ph]

    Args:
        mf : an :class:`SCF` object

        aolabels : string or a list of strings
            AO labels for AO active space

    Kwargs:
        threshold : float
            Tructing threshold of the AO-projector above which AOs are kept in
            the active space.
        minao : str
            A reference AOs for AVAS.
        with_iao : bool
            Whether to use IAO localization to construct the reference active AOs.
        openshelloption : int
            How to handle singly-occupied orbitals in the active space. The
            singly-occupied orbitals are projected as part of alpha orbitals
            if openshelloption=2, or completely kept in active space if
            openshelloption=3.  See Section III.E option 2 or 3 of the
            reference paper for more details.
        canonicalize : bool
            Orbitals defined in AVAS method are local orbitals.  Symmetrizing
            the core, active and virtual space.

    Returns:
        active-space-size, #-active-electrons, orbital-initial-guess-for-CASCI/CASSCF

    Examples:

    >>> from pyscf import gto, scf, mcscf
    >>> from pyscf.mcscf import avas
    >>> mol = gto.M(atom='Cr 0 0 0; Cr 0 0 1.6', basis='ccpvtz')
    >>> mf = scf.RHF(mol).run()
    >>> ncas, nelecas, mo = avas.avas(mf, ['Cr 3d', 'Cr 4s'])
    >>> mc = mcscf.CASSCF(mf, ncas, nelecas).run(mo)
    '''
    if isinstance(verbose, logger.Logger):
        log = verbose
    elif verbose is not None:
        log = logger.Logger(mf.stdout, verbose)
    else:
        log = logger.Logger(mf.stdout, mf.verbose)
    mol = mf.mol

    log.info('\n** AVAS **')
    if isinstance(mf, scf.uhf.UHF):
        log.note('UHF/UKS object is found.  AVAS takes alpha orbitals only')
        mo_coeff = mf.mo_coeff[0]
        mo_occ = mf.mo_occ[0]
        mo_energy = mf.mo_energy[0]
        assert (openshelloption != 1)
    else:
        mo_coeff = mf.mo_coeff
        mo_occ = mf.mo_occ
        mo_energy = mf.mo_energy
    nocc = numpy.count_nonzero(mo_occ != 0)
    ovlp = mol.intor_symmetric('cint1e_ovlp_sph')
    log.info('  Total number of HF MOs  is equal to    %d', mo_coeff.shape[1])
    log.info('  Number of occupied HF MOs is equal to  %d', nocc)

    mol = mf.mol
    pmol = mol.copy()
    pmol.atm = mol._atm
    pmol.basis = minao
    pmol.build(False, False)

    if isinstance(aolabels, str):
        aolabels = re.sub(' +', ' ', aolabels.strip(), count=1)
        baslst = [
            i for i, s in enumerate(pmol.spherical_labels(1)) if aolabels in s
        ]
    elif isinstance(aolabels[0], str):
        aolabels = [re.sub(' +', ' ', x.strip(), count=1) for x in aolabels]
        baslst = [
            i for i, s in enumerate(pmol.spherical_labels(1))
            if any(x in s for x in aolabels)
        ]
    else:
        raise RuntimeError
    baslst = numpy.asarray(baslst)
    log.info('reference AO indices for %s %s: %s', minao, aolabels, baslst)

    if with_iao:
        from pyscf.lo import iao
        c = iao.iao(mol, mo_coeff[:, :nocc], minao)[:, baslst]
        s2 = reduce(numpy.dot, (c.T, ovlp, c))
        s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff))
    else:
        s2 = pmol.intor_symmetric('cint1e_ovlp_sph')[baslst][:, baslst]
        s21 = gto.intor_cross('cint1e_ovlp_sph', pmol, mol)[baslst]
        s21 = numpy.dot(s21, mo_coeff)
    sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True))

    if openshelloption == 2:
        wocc, u = numpy.linalg.eigh(sa[:nocc, :nocc])
        log.info('Option 2: threshold %s', threshold)
        ncas_occ = (wocc > threshold).sum()
        nelecas = mol.nelectron - (wocc < threshold).sum() * 2
        mocore = mo_coeff[:, :nocc].dot(u[:, wocc < threshold])
        mocas = mo_coeff[:, :nocc].dot(u[:, wocc > threshold])

        wvir, u = numpy.linalg.eigh(sa[nocc:, nocc:])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack(
            (mocas, mo_coeff[:, nocc:].dot(u[:, wvir > threshold])))
        movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold])
        ncas = mocas.shape[1]

    elif openshelloption == 3:
        docc = nocc - mol.spin
        wocc, u = numpy.linalg.eigh(sa[:docc, :docc])
        log.info('Option 3: threshold %s, num open shell %d', threshold,
                 mol.spin)
        ncas_occ = (wocc > threshold).sum()
        nelecas = mol.nelectron - (wocc < threshold).sum() * 2
        mocore = mo_coeff[:, :docc].dot(u[:, wocc < threshold])
        mocas = mo_coeff[:, :docc].dot(u[:, wocc > threshold])

        wvir, u = numpy.linalg.eigh(sa[nocc:, nocc:])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas, mo_coeff[:, docc:nocc],
                              mo_coeff[:, nocc:].dot(u[:, wvir > threshold])))
        movir = mo_coeff[:, nocc:].dot(u[:, wvir < threshold])
        ncas = mocas.shape[1]

    log.debug('projected occ eig %s', wocc[::-1])
    log.debug('projected vir eig %s', wvir[::-1])
    log.info('Active from occupied = %d , eig %s', ncas_occ,
             wocc[wocc > threshold][::-1])
    log.info('Inactive from occupied = %d', mocore.shape[1])
    log.info('Active from unoccupied = %d , eig %s', ncas_vir,
             wvir[wvir > threshold][::-1])
    log.info('Inactive from unoccupied = %d', movir.shape[1])
    log.info('Dimensions of active %d', ncas)
    nalpha = (nelecas + mol.spin) // 2
    nbeta = nelecas - nalpha
    log.info('# of alpha electrons %d', nalpha)
    log.info('# of beta electrons %d', nbeta)

    if canonicalize:
        from pyscf.mcscf import dmet_cas

        def trans(c):
            if c.shape[1] == 0:
                return c
            else:
                csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff))
                fock = numpy.dot(csc * mo_energy, csc.T)
                e, u = scipy.linalg.eigh(fock)
                return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log)

        mo = numpy.hstack([trans(mocore), trans(mocas), trans(movir)])
    else:
        mo = numpy.hstack((mocore, mocas, movir))
    return ncas, nelecas, mo
Exemplo n.º 17
0
def _kernel(avas_obj):
    mf = avas_obj._scf
    mol = mf.mol
    log = logger.new_logger(avas_obj)
    log.info('\n** AVAS **')

    assert avas_obj.openshell_option != 1

    if isinstance(mf, scf.uhf.UHF):
        log.note('UHF/UKS object is found.  AVAS takes alpha orbitals only')
        mo_coeff = mf.mo_coeff[0]
        mo_occ = mf.mo_occ[0]
        mo_energy = mf.mo_energy[0]
    else:
        mo_coeff = mf.mo_coeff
        mo_occ = mf.mo_occ
        mo_energy = mf.mo_energy

    ncore = avas_obj.ncore
    nocc = numpy.count_nonzero(mo_occ != 0)
    ovlp = mol.intor_symmetric('int1e_ovlp')
    log.info('  Total number of HF MOs  is equal to    %d' ,mo_coeff.shape[1])
    log.info('  Number of occupied HF MOs is equal to  %d', nocc)

    mol = mf.mol
    pmol = mol.copy()
    pmol.atom = mol._atom
    pmol.unit = 'B'
    pmol.symmetry = False
    pmol.basis = avas_obj.minao
    pmol.build(False, False)

    baslst = pmol.search_ao_label(avas_obj.aolabels)
    log.info('reference AO indices for %s %s: %s',
             avas_obj.minao, avas_obj.aolabels, baslst)

    if avas_obj.with_iao:
        from pyscf.lo import iao
        c = iao.iao(mol, mo_coeff[:,ncore:nocc], avas_obj.minao)[:,baslst]
        s2 = reduce(numpy.dot, (c.T, ovlp, c))
        s21 = reduce(numpy.dot, (c.T, ovlp, mo_coeff[:, ncore:]))
    else:
        s2 = pmol.intor_symmetric('int1e_ovlp')[baslst][:,baslst]
        s21 = gto.intor_cross('int1e_ovlp', pmol, mol)[baslst]
        s21 = numpy.dot(s21, mo_coeff[:, ncore:])
    sa = s21.T.dot(scipy.linalg.solve(s2, s21, sym_pos=True))

    threshold = avas_obj.threshold
    if avas_obj.openshell_option == 2:
        wocc, u = numpy.linalg.eigh(sa[:(nocc-ncore), :(nocc-ncore)])
        log.info('Option 2: threshold %s', threshold)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        mocore = mo_coeff[:,ncore:nocc].dot(u[:,wocc<threshold])
        mocas = mo_coeff[:,ncore:nocc].dot(u[:,wocc>=threshold])

        wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas,
                              mo_coeff[:,nocc:].dot(u[:,wvir>=threshold])))
        movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold])
        ncas = mocas.shape[1]

        occ_weights = numpy.hstack([wocc[wocc<threshold], wocc[wocc>=threshold]])
        vir_weights = numpy.hstack([wvir[wvir>=threshold], wvir[wvir<threshold]])

    elif avas_obj.openshell_option == 3:
        docc = nocc - mol.spin
        wocc, u = numpy.linalg.eigh(sa[:(docc-ncore),:(docc-ncore)])
        log.info('Option 3: threshold %s, num open shell %d', threshold, mol.spin)
        ncas_occ = (wocc > threshold).sum()
        nelecas = (mol.nelectron - ncore * 2) - (wocc < threshold).sum() * 2
        mocore = mo_coeff[:,ncore:docc].dot(u[:,wocc<threshold])
        mocas = mo_coeff[:,ncore:docc].dot(u[:,wocc>=threshold])

        wvir, u = numpy.linalg.eigh(sa[(nocc-ncore):,(nocc-ncore):])
        ncas_vir = (wvir > threshold).sum()
        mocas = numpy.hstack((mocas,
                              mo_coeff[:,docc:nocc],
                              mo_coeff[:,nocc:].dot(u[:,wvir>=threshold])))
        movir = mo_coeff[:,nocc:].dot(u[:,wvir<threshold])
        ncas = mocas.shape[1]

        occ_weights = numpy.hstack([wocc[wocc<threshold], numpy.ones(nocc-docc),
                                    wocc[wocc>=threshold]])
        vir_weights = numpy.hstack([wvir[wvir>=threshold], wvir[wvir<threshold]])
    else:
        raise RuntimeError(f'Unknown option openshell_option {avas_obj.openshell_option}')

    log.debug('projected occ eig %s', occ_weights)
    log.debug('projected vir eig %s', vir_weights)
    log.info('Active from occupied = %d , eig %s', ncas_occ, occ_weights[occ_weights>=threshold])
    log.info('Inactive from occupied = %d', mocore.shape[1])
    log.info('Active from unoccupied = %d , eig %s', ncas_vir, vir_weights[vir_weights>=threshold])
    log.info('Inactive from unoccupied = %d', movir.shape[1])
    log.info('Dimensions of active %d', ncas)
    nalpha = (nelecas + mol.spin) // 2
    nbeta = nelecas - nalpha
    log.info('# of alpha electrons %d', nalpha)
    log.info('# of beta electrons %d', nbeta)

    mofreeze = mo_coeff[:,:ncore]
    if avas_obj.canonicalize:
        from pyscf.mcscf import dmet_cas

        def trans(c):
            if c.shape[1] == 0:
                return c
            else:
                csc = reduce(numpy.dot, (c.T, ovlp, mo_coeff))
                fock = numpy.dot(csc*mo_energy, csc.T)
                e, u = scipy.linalg.eigh(fock)
                return dmet_cas.symmetrize(mol, e, numpy.dot(c, u), ovlp, log)
        if ncore > 0:
            mofreeze = trans(mofreeze)
        mocore = trans(mocore)
        mocas = trans(mocas)
        movir = trans(movir)
    mo = numpy.hstack((mofreeze, mocore, mocas, movir))
    return ncas, nelecas, mo, occ_weights, vir_weights