示例#1
0
    def compute_coupling(
            self,
            S,
            indexA,
            indexB,
            doETF=False,  # Do Joe Subotnik ETF?
    ):

        if indexA == indexB:
            raise ValueError('indexA == indexB: you want a gradient?')

        # Sizes
        nao = self.sizes['nao']
        nmo = self.sizes['nmo']
        nocc = self.sizes['nocc']
        nvir = self.sizes['nvir']

        # Energy difference between states
        dE = self.evals[S][indexB] - self.evals[S][indexA]
        CA = self.evecs[S][indexA]
        CB = self.evecs[S][indexB]
        Cocc = self.tensors['Cocc']
        Cvir = self.tensors['Cvir']

        # Difference transition OPDM in AO basis
        D1dao = ls.Tensor((nao, ) * 2)
        if CA is None:
            # <0|B>
            # T_jb = + sqrt(2) CB_jb
            D1dao[...] += np.sqrt(2.0) * ls.Tensor.chain([Cocc, CB, Cvir],
                                                         [False, False, True])
        elif CB is None:
            # <A|0>
            # T_ai = + sqrt(2) CA_ia
            D1dao[...] += np.sqrt(2.0) * ls.Tensor.chain([Cvir, CA, Cocc],
                                                         [False, True, True])
        else:
            # T_ab <- + CA_ia CB_ib
            D1dao[...] = ls.Tensor.chain([Cvir, CA, CB, Cvir],
                                         [False, True, False, True])
            # T_ji <- - CA_ia CB_ja (watch the tanspose!)
            D1dao[...] -= ls.Tensor.chain([Cocc, CB, CA, Cocc],
                                          [False, False, True, True])

        raise ValueError("Not implemented")
示例#2
0
    def orbital_atomic_charges(
        self,
        C, 
        ):

        """ Compute the orbital atomic charges Q_A^i for a set of orbitals C_mi

        Only the electronic contribution is considered.

        Params:
            C (ls.Tensor of shape (nao, ni)) - Orbital coefficients
        Returns:
            Q (ls.Tensor of shape (natom, ni)) - Orbital atomic charges
        """

        L = ls.Tensor.chain([C,self.S,self.A],[True,False,False])
        Q = ls.Tensor((self.minbasis.natom,C.shape[1]), 'Q IBO')
        for A, inds2 in enumerate(self.minao_inds):
            Q[A,:] = np.sum(L[:,inds2]**2,1)
        return Q
示例#3
0
def test_casbox_orbital_transformation_det(
    S=0,
    M=6,
    Na=3,
    Nb=3,
    H1=None,
    I1=None,
    ):

    # Random rotation matrix
    A = ls.Tensor.array(np.random.rand(M, M) + 0.3 * np.eye(M))
    Q, R = sp.qr(A)
    Q = ls.Tensor.array(Q)
    # Random permutation matrix (forces pivoting)
    perm = np.random.permutation(list(range(M)))
    Q2 = ls.Tensor((M,)*2)
    for k, k2 in enumerate(perm):
        Q2[k,k2] = 1.0
    # Orbital transformation matrix (some rotation, required pivoting in LU)
    Q3 = ls.Tensor.chain([Q, Q2], [False, False])

    # Integrals in transformed basis
    H2 = ls.Tensor.array(np.einsum('ae,bf,ab->ef', Q3, Q3, H1))
    I2 = ls.Tensor.array(np.einsum('ae,bf,cg,dh,abcd->efgh', Q3, Q3, Q3, Q3, I1))

    casbox1 = ls.CASBox(M, Na, Nb, H1, I1)
    casbox2 = ls.CASBox(M, Na, Nb, H2, I2)

    ebox1 = ls.ExplicitCASBox(casbox1)
    ebox2 = ls.ExplicitCASBox(casbox2)

    evec1 = ebox1.evec(S,0)
    evec2 = ebox2.evec(S,0)

    evec1d = casbox1.CSF_basis(S).transform_CSF_to_det(evec1)
    evec2d = casbox2.CSF_basis(S).transform_CSF_to_det(evec2)

    evec1dp = casbox1.orbital_transformation_det(Q3, evec1d)
    O = evec1dp.vector_dot(evec2d)
    OK = abs(abs(O) - 1.0) < 1.0E-13
    return OK
示例#4
0
    def atomic_charges(
        self,
        D,
        ):

        """ Compute the atomic charges Q_A for an OPDM D_mn.

        Only the electronic contribution is considered.

        Params:
            D (ls.Tensor of shape (nao, nao)) - density matrix
        Returns:
            Q (ls.Tensor of shape (natom,)) - atomic charges
        """

        V = ls.Tensor.chain([self.A, self.S, D, self.S, self.A], [True, False, False, False, False])
        V2 = np.diag(V)
        Q = ls.Tensor((self.minbasis.natom,), 'Q IBO')
        for A, inds2 in enumerate(self.minao_inds):
            Q[A] = np.sum(V2[inds2])
        return Q
示例#5
0
 def compute_external_potential(
     self,
     pairlist,
 ):
     """ Return the complete external potential of this Geometry, including the
         nuclear attraction potential of the QM molecule, and any external
         potential terms involving other structures in the Geometry. """
     V = ls.Tensor((pairlist.basis1.nao, pairlist.basis2.nao))
     ls.IntBox.potential(self.resources, self.ewald, pairlist,
                         self.qm_molecule_ecp.xyzZ, V)
     # QM/MM
     if self.qmmm:
         V[...] += self.qmmm.compute_mm_potential(self.resources,
                                                  self.ewald, pairlist)
     # ECP
     if self.ecp:
         V[...] += ls.IntBox.ecp(
             self.resources,
             pairlist,
             self.ecp,
             self.options['threecp'],
         )
     return V
示例#6
0
    O 0.000000000000  0.000000000000 -0.129476890157
    H 0.000000000000 -1.494186750504  1.027446102928
    H 0.000000000000  1.494186750504  1.027446102928""",
                               name='h2o',
                               scale=1.0)

bas = ls.Basis.from_gbs_file(mol, 'cc-pvdz')

minao = ls.Basis.from_gbs_file(mol, 'cc-pvdz-minao')

res = ls.ResourceList.build_cpu()

nocc = ls.SAD.sad_nocc_neutral(mol)
print(nocc)

Q = ls.Tensor((3, ))
Q[0] = 4.5
Q[1] = 0.5
Q[2] = 0.5

nocc = ls.SAD.sad_nocc(mol, Q)
print(nocc)

print(ls.SAD.sad_nocc_atoms())

S = ls.IntBox.overlap(res, ls.PairList.build_schwarz(bas, bas, True, 1.0E-14))
print(S)

C = ls.SAD.sad_orbitals(res, nocc, minao, bas)
print(C)
示例#7
0
    def __init__(
        self,
        options, # A dictionary of user options
        ):

        # => Default Options <= #

        self.options = RHF.options.copy()
    
        # => Override Options <= #

        for key, val in options.items():
            if not key in list(self.options.keys()):
                raise ValueError('Invalid user option: %s' % key)
            self.options[key] = val 

        # => Useful Registers <= #

        self.sizes = {}
        self.scalars = {}
        self.tensors = {}

        # => Title Bars <= #

        print('==> RHF <==\n')

        # => Problem Geometry <= #

        self.resources = self.options['resources']
        self.molecule = self.options['molecule']
        self.basis = self.options['basis']
        self.minbasis = self.options['minbasis']

        print(self.resources)
        print(self.molecule)
        print(self.basis)
        print(self.minbasis)

        self.sizes['natom'] = self.molecule.natom
        self.sizes['nao'] = self.basis.nao
        self.sizes['nmin'] = self.minbasis.nao

        # => Nuclear Repulsion <= #

        self.scalars['Enuc'] = self.molecule.nuclear_repulsion_energy()
        print('Nuclear Repulsion Energy = %20.14f\n' % self.scalars['Enuc'])

        # => Integral Thresholds <= #

        print('Integral Thresholds:')
        print('  Threshold PQ = %11.3E' % self.options['thre_pq'])
        print('  Threshold DP = %11.3E' % self.options['thre_dp'])
        print('  Threshold SP = %11.3E' % self.options['thre_sp'])
        print('')

        # => PairList <= #

        self.pairlist = ls.PairList.build_schwarz(
            self.basis,
            self.basis,
            True,
            self.options['thre_pq'])
        print(self.pairlist)

        # => One-Electron Integrals <= #

        print('One-Electron Integrals:\n')
        self.tensors['S'] = ls.IntBox.overlap(
            self.resources,
            self.pairlist)
        self.tensors['T'] = ls.IntBox.kinetic(
            self.resources,
            self.pairlist)
        self.tensors['V'] = ls.IntBox.potential(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            self.molecule.xyzZ)
        self.tensors['H'] = ls.Tensor.array(self.tensors['T'].np + self.tensors['V'].np)

        # => Canonical Orthogonalization <= #

        print('Canonical Orthogonalization:')
        print('  Threshold = %11.3E' % self.options['thre_canonical'])
        self.tensors['X'] = ls.Tensor.canonical_orthogonalize(self.tensors['S'], self.options['thre_canonical'])
        self.sizes['nmo'] = self.tensors['X'].shape[1]
        print('  Nmo       = %11d' % self.sizes['nmo'])
        print('  Ndiscard  = %11d' % (self.sizes['nao'] - self.sizes['nmo']))
        print('')

        # => SAD Guess <= #

        print('SAD Guess:\n')
        self.tensors['CSAD'] = ls.SAD.orbitals(
            self.resources, 
            self.molecule,
            self.basis,
            self.minbasis)
        self.tensors['DSAD'] = ls.Tensor.chain([self.tensors['CSAD'],self.tensors['CSAD']],[False,True])

        # => DIIS <= #

        diis = ls.DIIS(
            self.options['diis_max_vecs'],
            )
        print(diis)
        
        # => Determine number of electrons (and check for integral singlet) <= #

        if self.options['nocc'] is None:
            if self.options['charge'] is None:
                charge = self.molecule.charge
            else:
                charge = self.options['charge']
            nelec = self.molecule.nuclear_charge - charge
            nalpha = 0.5 * nelec
            if nalpha != round(nalpha):
                raise ValueError('Cannot do fractional electrons. Possibly charge/multiplicity are wrong.') 
            self.sizes['nocc'] = int(nalpha)
        else:
            self.sizes['nocc'] = int(self.options['nocc'])

        self.sizes['nvir'] = self.sizes['nmo'] - self.sizes['nocc']
        print('Orbital Spaces:')
        print('  Nocc = %d' % self.sizes['nocc'])
        print('  Nvir = %d' % self.sizes['nvir'])
        print('') 

        # ==> SCF Iterations <== #

        print('Convergence Options:')
        print('  Max Iterations = %11d' % self.options['maxiter'])
        print('  E Convergence  = %11.3E' % self.options['e_convergence'])
        print('  G Convergence  = %11.3E' % self.options['g_convergence'])
        print('')

        self.tensors['D'] = ls.Tensor.array(self.tensors['DSAD'])
        Eold = 0.
        converged = False
        print('SCF Iterations:\n')
        print('%4s: %24s %11s %11s' % ('Iter', 'Energy', 'dE', 'dG'))
        for iter in range(self.options['maxiter']):
            
            # => Compute J/K Matrices <= #

            import time
            start = time.time()
            self.tensors['J'] = ls.IntBox.coulomb(
                self.resources,
                ls.Ewald.coulomb(),
                self.pairlist,
                self.pairlist,
                self.tensors['D'],
                self.options['thre_sp'],    
                self.options['thre_dp'],    
                )
            print('J: %11.3E' % (time.time() - start))
            start = time.time()
            self.tensors['K'] = ls.IntBox.exchange(
                self.resources,
                ls.Ewald.coulomb(),
                self.pairlist,
                self.pairlist,
                self.tensors['D'],
                True,
                self.options['thre_sp'],    
                self.options['thre_dp'],    
                )
            print('K: %11.3E' % (time.time() - start))

            start = time.time()

            # => Build Fock Matrix <= #            

            self.tensors['F'] = ls.Tensor.array(self.tensors['H'])
            self.tensors['F'].np[...] += 2. * self.tensors['J'].np
            self.tensors['F'].np[...] -= 1. * self.tensors['K'].np

            # => Compute Energy <= #

            self.scalars['Escf'] = self.scalars['Enuc'] + \
                self.tensors['D'].vector_dot(self.tensors['H']) + \
                self.tensors['D'].vector_dot(self.tensors['F']) 
            dE = self.scalars['Escf'] - Eold
            Eold = self.scalars['Escf']

            # => Compute Orbital Gradient <= #

            G1 = ls.Tensor.chain([self.tensors[x] for x in ['X', 'S', 'D', 'F', 'X']], [True] + [False]*4)
            G1.antisymmetrize()
            G1[...] *= 2.0
            self.tensors['G'] = G1
            dG = np.max(np.abs(self.tensors['G']))

            # => Print Iteration <= #
            
            print('%4d: %24.16E %11.3E %11.3E' % (iter, self.scalars['Escf'], dE, dG))
    
            # => Check Convergence <= #
    
            if iter > 0 and abs(dE) < self.options['e_convergence'] and dG < self.options['g_convergence']:
                converged = True
                break

            # => DIIS Fock Matrix <= #

            if iter > 0:
                self.tensors['F'] = diis.iterate(self.tensors['F'], self.tensors['G'])

            # => Diagonalize Fock Matrix <= #

            F2 = ls.Tensor.chain([self.tensors[x] for x in ['X', 'F', 'X']],[True,False,False])
            e2, U2 = ls.Tensor.eigh(F2)
            C2 = ls.Tensor.chain([self.tensors['X'], U2],[False,False])
            e2.name = 'eps'
            C2.name = 'C'
            self.tensors['C'] = C2
            self.tensors['eps'] = e2
            
            # => Aufbau Occupy Orbitals <= #

            self.tensors['Cocc'] = ls.Tensor((self.sizes['nao'], self.sizes['nocc']),'Cocc')
            self.tensors['Cocc'].np[...] = self.tensors['C'].np[:,:self.sizes['nocc']]

            # => Compute Density Matrix <= #

            self.tensors['D'] = ls.Tensor.chain([self.tensors['Cocc'], self.tensors['Cocc']], [False, True])
            print('T: %11.3E' % (time.time() - start))

        # => Print Convergence <= #
        
        print('')
        if converged:
            print('SCF Converged\n')
        else:
            print('SCF Failed\n')
        
        # => Print Final Energy <= #
        
        print('SCF Energy = %24.16E\n' % self.scalars['Escf'])

        # => Cache the Orbitals/Eigenvalues (for later use) <= #

        self.tensors['Cvir'] = ls.Tensor((self.sizes['nao'], self.sizes['nvir']), 'Cvir')
        self.tensors['Cvir'].np[...] = self.tensors['C'].np[:,self.sizes['nocc']:]
        
        self.tensors['eps_occ'] = ls.Tensor((self.sizes['nocc'],), 'eps_occ')
        self.tensors['eps_occ'].np[...] = self.tensors['eps'].np[:self.sizes['nocc']]
        
        self.tensors['eps_vir'] = ls.Tensor((self.sizes['nvir'],), 'eps_vir')
        self.tensors['eps_vir'].np[...] = self.tensors['eps'].np[self.sizes['nocc']:]

        # => Print Orbital Energies <= #

        print(self.tensors['eps_occ'])
        print(self.tensors['eps_vir'])

        # => Trailer Bars <= #

        print('"I love it when a plan comes together!"')
        print('        --LTC John "Hannibal" Smith\n')
        print('==> End RHF <==\n')
示例#8
0
    def diffraction_pattern(
        self,
        S,
        index1,
        index2,
        ):

        """ Compute ab initio elastic or inelastic diffraction pattern for xray
            or ued probes, using a variety of computational methods.

        If it is tractable to use moments to exactly compute the scattering
        signal (isotropic, parallel, perpendicular cases) this is done first.
    
        If this is not tractable, the full diffraction pattern is directly
        computed.

        Params:
            S (int) - spin index
            index1 (int) - index of bra state
            index2 (int) - index of ket state

        Elastic scattering is assumed if index1==index2 (scattering off of total state density)
        Inelastic scattering is assumed if index1!=index2 (scattering off of transition density)

        Returns: 
            I (ls.Tensor of shape (ns,neta)) - diffraction pattern.
        """
        
        # First try to compute via moments
        if self.anisotropy in ['isotropic', 'parallel', 'perpendicular']:
            IM = self.diffraction_moments(S=S,index1=index1,index2=index2)
            return AISCAT.pattern_from_moments(IM,eta=self.eta)
        # Otherwise, must compute aligned scattering signal
        # Grid density collocation
        xyzq = self.compute_xyzq(
            D=self.lot.compute_opdm_ao(S, index1, index2),
            include_nuclei=self.mode=='ued' and index1==index2,
            )
        print('Grid Density: %12.6f\n' % np.sum(xyzq[:,3]))
        # Scattering collocation points
        theta = 2.0 * np.arcsin(self.s[...] * self.L / (4.0 * np.pi))
        tt, ee = np.meshgrid(theta, self.eta, indexing='ij')
        ss, ee = np.meshgrid(self.s, self.eta, indexing='ij')
        # Compute scattering vectors
        sx = ss * np.cos(tt / 2.0) * np.sin(ee)
        sy = ss * np.sin(tt / 2.0) 
        sz = ss * np.cos(tt / 2.0) * np.cos(ee)
        # Pack for LS 
        sxyz = ls.Tensor((sx.size,3))
        sxyz[:,0] = np.ravel(sx)
        sxyz[:,1] = np.ravel(sy)
        sxyz[:,2] = np.ravel(sz)
        # Diffraction function
        if self.algorithm == 'cpu': fun = ls2.aligned_diffraction2
        elif self.algorithm == 'gpu_f_f': fun = ls2.aligned_diffraction_gpu_f_f
        elif self.algorithm == 'gpu_f_d': fun = ls2.aligned_diffraction_gpu_f_d
        elif self.algorithm == 'gpu_d_d': fun = ls2.aligned_diffraction_gpu_d_d
        # Compute diffraction pattern
        I = fun(
            self.lot.geometry.resources,
            sxyz,
            xyzq,
            )
        # Make sure return is (ns, neta)
        I = ls.Tensor.array(np.reshape(I, (self.s.shape[0], self.eta.shape[0])))
        # 1/s^4 weight in UED
        if self.mode == 'ued':
            I[...] /= ss**4
        return I
示例#9
0
    def compute_overlap(
        cisA,
        cisB,
        S,
    ):
        """ Compute the overlap between all states in spin block S in two CIS object.

        Params:
            cisA (CIS) - the bra-side CIS object
            cisB (CIS) - the ket-side CIS object
            S (int) - the spin index
        Returns:
            O (ls.Tensor, shape (nstateA, nstateB)) - the overlap matrix elements
        """

        # TODO: Benchmark these overlaps

        # Build the overlap integrals (p|q') in the AO basis
        pairlist = ls.PairList.build_schwarz(cisA.basis, cisB.basis, False,
                                             cisA.pairlist.thre)
        Sao = ls.IntBox.overlap(cisA.resources, pairlist)

        # Build the orbital overlap integrals in the occupied and active blocks of the MO basis
        Sij = ls.Tensor.chain(
            [cisA.tensors['Cocc'], Sao, cisB.tensors['Cocc']],
            [True, False, False])
        Sib = ls.Tensor.chain(
            [cisA.tensors['Cocc'], Sao, cisB.tensors['Cvir']],
            [True, False, False])
        Saj = ls.Tensor.chain(
            [cisA.tensors['Cvir'], Sao, cisB.tensors['Cocc']],
            [True, False, False])
        Sab = ls.Tensor.chain(
            [cisA.tensors['Cvir'], Sao, cisB.tensors['Cvir']],
            [True, False, False])

        # Compute the overlap inverse and determinant in the core space
        detC = Sij.invert_lu()

        # Form modified metric integrals
        Mij = Sij
        Mia = ls.Tensor((cisA.sizes['nocc'], cisA.sizes['nvir']))
        Mjb = ls.Tensor((cisB.sizes['nocc'], cisB.sizes['nvir']))
        Mab = ls.Tensor.array(Sab)
        if detC != 0.0:
            # Mai[...] = ls.Tensor.chain([Saj, Mij], [False, False])
            Mia[...] = ls.Tensor.chain(
                [Mij, Saj],
                [True, True])  # Transposed from the notes for easier dot
            Mjb[...] = ls.Tensor.chain([Mij, Sib], [False, False])
            Mab[...] -= ls.Tensor.chain([Saj, Mjb], [False, False])

        # Compute the intermediates for connected overlap term
        A2s = []
        for A, evecA in enumerate(cisA.evecs[S]):
            if S == 0 and A == 0:
                A2s.append(None)
                continue
            A2s.append(ls.Tensor.chain([evecA, Mab], [False, False]))
        B2s = []
        for B, evecB in enumerate(cisB.evecs[S]):
            if S == 0 and B == 0:
                B2s.append(None)
                continue
            B2s.append(ls.Tensor.chain([Mij, evecB], [True, False]))

        # Compute the overlaps
        O = ls.Tensor((len(cisA.evecs[S]), len(cisB.evecs[S])))
        for A, evecA in enumerate(cisA.evecs[S]):
            for B, evecB in enumerate(cisB.evecs[S]):
                if S == 0 and A == 0 and B == 0:
                    # <0|0>
                    O[A, B] = 1.0
                    continue
                if S == 0 and A == 0:
                    # <0|B>
                    O[A, B] = np.sqrt(2.0) * evecB.vector_dot(Mjb)
                    continue
                if S == 0 and B == 0:
                    # <A|0>
                    O[A, B] = np.sqrt(2.0) * evecA.vector_dot(Mia)
                    continue
                else:
                    # <A|B>
                    O[A, B] = A2s[A].vector_dot(B2s[B])
                    if S == 0:
                        O[A, B] += 2.0 * evecA.vector_dot(
                            Mia) * evecB.vector_dot(Mjb)

        # Account for the core overlap
        O[...] *= detC**2

        return O
示例#10
0
    def compute_gradient(
        self,
        S,
        index,
    ):

        # Handle the reference gradient separately
        if S == 0 and index == 0: return self.reference.compute_gradient()

        # Sizes
        nao = self.sizes['nao']
        nmo = self.sizes['nmo']
        nocc = self.sizes['nocc']
        nvir = self.sizes['nvir']

        # CI vector and orbitals
        Cvec = self.evecs[S][index]
        C = self.tensors['C']
        Cocc = self.tensors['Cocc']
        Cvir = self.tensors['Cvir']

        # Difference OPDM in AO basis
        D1dao = ls.Tensor.chain([Cvir, Cvec, Cvec, Cvir],
                                [False, True, False, True])
        D1dao[...] -= ls.Tensor.chain([Cocc, Cvec, Cvec, Cocc],
                                      [False, False, True, True])

        # Total OPDM in AO basis
        D1ao = ls.Tensor.chain([Cocc, Cocc], [False, True])
        D1ao[...] *= 2.0
        D1ao[...] += D1dao

        # CI Vector in AO basis
        C1ao = ls.Tensor.chain([Cocc, Cvec, Cvir], [False, False, True])

        # => J/K contributions <= #

        JD1 = ls.IntBox.coulomb(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            self.pairlist,
            D1dao,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        KD1 = ls.IntBox.exchange(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            self.pairlist,
            D1dao,
            True,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        JC1 = ls.IntBox.coulomb(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            self.pairlist,
            C1ao,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        KC1 = ls.IntBox.exchange(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            self.pairlist,
            C1ao,
            False,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )

        # TODO: The following manipulations can be *much* faster if one exploits block sparsity

        # => Fock Matrices (Long Way) <= #

        FO2 = ls.Tensor.chain([C, self.reference.tensors['F'], C],
                              [True, False, False])
        FD2 = ls.Tensor.chain([C, JD1, C], [True, False, False])
        FD2.np[...] -= 0.5 * ls.Tensor.chain([C, KD1, C],
                                             [True, False, False]).np
        FA2 = ls.Tensor.array(FO2)
        FA2.np[...] += FD2

        # => MO-Basis OPDMs (Long Way) <= #

        D2 = ls.Tensor((nmo, ) * 2)
        D2[nocc:, nocc:] += ls.Tensor.chain([Cvec, Cvec], [True, False])
        D2[:nocc, :nocc] -= ls.Tensor.chain([Cvec, Cvec], [False, True])
        A2 = ls.Tensor.array(np.diag(self.reference.tensors['n']))
        A2[...] *= 2.0
        A2[...] += D2

        # => MO-Basis Amplitudes (Long Way) <= #

        C2 = ls.Tensor((nmo, ) * 2)
        C2[:nocc, nocc:] = Cvec

        # => Lagrangian (Long Way) <= #

        X = ls.Tensor([self.sizes['nmo']] * 2)
        X.np[...] += ls.Tensor.chain([A2, FA2], [False, False])
        X.np[...] -= ls.Tensor.chain([D2, FD2], [False, False])
        if S == 0:
            X.np[...] += 2. * ls.Tensor.chain([C2, C, JC1, C],
                                              [False, True, True, False]).np
            X.np[...] += 2. * ls.Tensor.chain([C2, C, JC1, C],
                                              [True, True, False, False]).np
        X.np[...] -= 1. * ls.Tensor.chain([C2, C, KC1, C],
                                          [False, True, True, False]).np
        X.np[...] -= 1. * ls.Tensor.chain([C2, C, KC1, C],
                                          [True, True, False, False]).np

        X[...] *= 2.0  # Spin-summed convention
        X = X.transpose(
        )  # Ed's convention: X_pq = 2 h_pr D1_qr + 4 (pr|st) D2_qrst

        # => TPDM Gradient <= #

        grads = collections.OrderedDict()
        # Coulomb integral gradient 1
        grads['J1'] = ls.IntBox.coulombGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            D1ao,
            D1ao,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['J1'].np[...] *= +0.5
        # Coulomb integral gradient 2
        grads['J2'] = ls.IntBox.coulombGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            D1dao,
            D1dao,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['J2'].np[...] *= -0.5
        # Exchange integral gradient 1
        grads['K1'] = ls.IntBox.exchangeGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            D1ao,
            D1ao,
            True,
            True,
            True,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['K1'].np[...] *= -0.25
        # Exchange integral gradient 2
        grads['K2'] = ls.IntBox.exchangeGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            D1dao,
            D1dao,
            True,
            True,
            True,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['K2'].np[...] *= +0.25
        # Coulomb integral gradient 3
        grads['J3'] = ls.IntBox.coulombGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            C1ao,
            C1ao,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['J3'].np[...] *= +2.0 if S == 0 else 0.0
        # Exchange integral gradient 2
        grads['K3'] = ls.IntBox.exchangeGrad(
            self.resources,
            ls.Ewald.coulomb(),
            self.pairlist,
            C1ao,
            C1ao,
            False,
            False,
            True,
            self.options['thre_sp'],
            self.options['thre_dp'],
        )
        grads['K3'].np[...] *= -1.0

        grad2e = ls.Tensor.zeros_like(grads['J1'])
        for grad in list(grads.values()):
            grad2e[...] += grad

        # for key, grad in grads.iteritems():
        #     grad.name = key
        #     print grad

        return self.reference.compute_hf_based_gradient(D1ao, X, grad2e)
示例#11
0
    def compute_dipoles(
        self,
        S,
        x0=0.0,
        y0=0.0,
        z0=0.0,
    ):
        """Calculates total and transition dipole moments for all states in
            spin block S.

        Params:
            S (int) - spin block index
            x0 (float) - origin
            y0 (float) - origin
            z0 (float) - origin

        Returns:
            XYZ ((nstates, nstates) ls.Tensor.array) - transition dipole matrix

        """

        # xyz dipole matrix in AO basis
        XYZ = ls.IntBox.dipole(
            self.resources,
            self.pairlist,
            x0,
            y0,
            z0,
        )

        evecs = self.evecs[S]
        nstate = len(evecs)
        Cocc = self.tensors['Cocc']
        Cvir = self.tensors['Cvir']

        XYZ3 = [ls.Tensor((nstate, nstate)) for x in range(3)]
        for A, evecA in enumerate(evecs):
            for B, evecB in enumerate(evecs):
                if A > B: continue
                if evecA is None and evecB is None: continue  # <0|D|0>
                if evecA is None:  # <0|D|0>
                    # D_ia = + sqrt(2) C_ia
                    Dao = ls.Tensor.chain([Cocc, evecB, Cvir],
                                          [False, False, True])
                    Dao[...] *= np.sqrt(2.0)
                else:
                    # D_ab = + C_ia C_ib
                    Dao = ls.Tensor.chain([Cvir, evecA, evecB, Cvir],
                                          [False, True, False, True])
                    # D_ij = - C_ja C_ia
                    Dao[...] -= ls.Tensor.chain([Cocc, evecB, evecA, Cocc],
                                                [False, False, True, True])

                # Dot with AB-basis
                XYZ3[0][A, B] = XYZ3[0][B, A] = Dao.vector_dot(XYZ[0])
                XYZ3[1][A, B] = XYZ3[1][B, A] = Dao.vector_dot(XYZ[1])
                XYZ3[2][A, B] = XYZ3[2][B, A] = Dao.vector_dot(XYZ[2])

        # Reference density matrix
        Dcore = ls.Tensor.chain([Cocc, Cocc], [False, True])
        Dcore[...] *= 2.0

        Xcore = Dcore.vector_dot(XYZ[0])
        Ycore = Dcore.vector_dot(XYZ[1])
        Zcore = Dcore.vector_dot(XYZ[2])

        # Get nuclear dipole
        for A, atomA in enumerate(self.qm_molecule.atoms):
            Xcore -= atomA.Z * (atomA.x - x0)
            Ycore -= atomA.Z * (atomA.y - y0)
            Zcore -= atomA.Z * (atomA.z - z0)

        for A in range(nstate):
            XYZ3[0][A, A] += Xcore
            XYZ3[1][A, A] += Ycore
            XYZ3[2][A, A] += Zcore

        return XYZ3
示例#12
0
    def compute_states(
        self,
        Sindex,
        nstate,
    ):

        # => Title <= #

        title = 'S=%d' % Sindex
        if self.print_level:
            print('=> %s States <=\n' % title)
        if Sindex not in [0, 2]:
            raise RuntimeError('Invalid Sindex for CIS: %d' % Sindex)

        # => Preconditioner <= #

        F = ls.Tensor((self.sizes['nocc'], self.sizes['nvir']))
        F.np[...] += np.einsum('i,a->ia', np.ones(self.sizes['nocc']),
                               self.tensors['eps_vir'])
        F.np[...] -= np.einsum('i,a->ia', self.tensors['eps_occ'],
                               np.ones(self.sizes['nvir']))

        # => Guess Vectors <= #

        nstate_new = nstate - 1 if Sindex == 0 else nstate
        nguess = min(self.sizes['nocc'] * self.sizes['nvir'],
                     nstate_new * self.options['nguess_per_root'])
        if self.print_level > 1:
            print('Guess Details:')
            print('  Nguess: %11d' % nguess)
        Forder = sorted([(x, xind[0], xind[1])
                         for xind, x in np.ndenumerate(F)])
        Cs = []
        for k in range(nguess):
            C = ls.Tensor((self.sizes['nocc'], self.sizes['nvir']))
            r = Forder[k][1]
            c = Forder[k][2]
            if self.print_level > 1:
                print('  %3d: %5d -> %-5d' % (k, r, c + self.sizes['nocc']))
            C.np[r, c] = +1.0
            Cs.append(C)
        if self.print_level > 1:
            print('')

        # => Convergence Info <= #

        if self.print_level > 1:
            print('Convergence:')
            print('  Maxiter: %11d' % self.options['maxiter'])
            print('')

        # => Davidson Object <= #

        dav = ls.Davidson(
            nstate_new,
            self.options['nmax'],
            self.options['r_convergence'],
            self.options['norm_cutoff'],
        )
        if self.print_level > 1:
            print(dav)

        # ==> Davidson Iterations <== #

        if self.print_level:
            print('%s CIS Iterations:\n' % title)
            print('%4s: %11s' % ('Iter', '|R|'))
        converged = False
        for iter in range(self.options['maxiter']):

            # Sigma Vector Builds
            Ss = [ls.Tensor.clone(C) for C in Cs]
            for C, S in zip(Cs, Ss):
                # One-Particle Term
                S.np[...] *= F
                # Two-Particle Term
                D = ls.Tensor.chain(
                    [self.tensors['Cocc'], C, self.tensors['Cvir']],
                    [False, False, True])
                if Sindex == 0:
                    J = ls.IntBox.coulomb(
                        self.resources,
                        ls.Ewald.coulomb(),
                        self.pairlist,
                        self.pairlist,
                        D,
                        self.options['thre_sp'],
                        self.options['thre_dp'],
                    )
                    J2 = ls.Tensor.chain(
                        [self.tensors['Cocc'], J, self.tensors['Cvir']],
                        [True, False, False])
                K = ls.IntBox.exchange(
                    self.resources,
                    ls.Ewald.coulomb(),
                    self.pairlist,
                    self.pairlist,
                    D,
                    False,
                    self.options['thre_sp'],
                    self.options['thre_dp'],
                )
                K2 = ls.Tensor.chain(
                    [self.tensors['Cocc'], K, self.tensors['Cvir']],
                    [True, False, False])
                if Sindex == 0:
                    S.np[...] += 2. * J2.np - K2.np
                else:
                    S.np[...] -= K2.np

            # Add vectors/diagonalize Davidson subspace
            Rs, Es = dav.add_vectors(Cs, Ss, use_disk=self.options['use_disk'])

            # Print Iteration Trace
            if self.print_level:
                print('%4d: %11.3E' % (iter, dav.max_rnorm))

            # Check for convergence
            if dav.is_converged:
                converged = True
                break

            # Precondition the desired residuals
            for R, E in zip(Rs, Es):
                R.np[...] /= -(F.np[...] - E)

            # Get new trial vectors
            Cs = dav.add_preconditioned(Rs, use_disk=self.options['use_disk'])

        # => Output Quantities <= #

        # Print Convergence
        if self.print_level:
            print('')
            if converged:
                print('%s CIS Converged\n' % title)
            else:
                print('%s CIS Failed\n' % title)

        # Cache quantities
        self.converged[Sindex] = converged
        self.evals[Sindex] = []
        self.evecs[Sindex] = []
        # Make allowance for the reference state
        if Sindex == 0:
            self.evals[Sindex] += [self.scalars['Escf']]
            self.evecs[Sindex] += [None]
        self.evals[Sindex] += [x + self.scalars['Escf'] for x in dav.evals]
        self.evecs[Sindex] += [
            ls.Storage.from_storage(x, Ss[0]) for x in dav.evecs
        ]

        # Print residuals
        if self.print_level:
            print('CIS %s Residuals:\n' % title)
            print('%4s: %11s' % ('I', '|R|'))
            for rind, r in enumerate(dav.rnorms):
                Soffset = 1 if Sindex == 0 else 0  # offset for the ground state
                print('%4d: %11.3E %s' %
                      (rind + Soffset, r, 'Converged' if
                       r < self.options['r_convergence'] else 'Not Converged'))
            print('')

        # Print energies
        if self.print_level:
            print('CIS %s Energies:\n' % title)
            print('%4s: %24s' % ('I', 'Total E'))
            for Eind, E in enumerate(self.evals[Sindex]):
                print('%4d: %24.16E' % (Eind, E))
            print('')

        if self.print_level:
            print('=> End %s States <=\n' % title)
示例#13
0
    def compute_cphf(
            self,
            G,  # A tensor which is nocc x nvir
            options={},  # Override options
    ):

        # => Override Options <= #

        options2 = options
        options = RHF.cphf_options.copy()
        for key, val in options2.items():
            if not key in list(options.keys()):
                raise ValueError('Invalid user option: %s' % key)
            options[key] = val

        # Check that G has correct shape
        G.shape_error((self.sizes['nocc'], self.sizes['nvir']))

        # => Header <= #

        #print '==> CPHF <==\n'

        # => DIIS <= #

        diis = ls.DIIS(options['diis_max_vecs'], )
        #print diis

        # => Preconditioner <= #

        # Fock-Matrix Contribution (Preconditioner)
        F = ls.Tensor((self.sizes['nocc'], self.sizes['nvir']), 'F')
        F.np[...] += np.einsum('i,a->ia', np.ones(self.sizes['nocc']),
                               self.tensors['eps_vir'])
        F.np[...] -= np.einsum('i,a->ia', self.tensors['eps_occ'],
                               np.ones(self.sizes['nvir']))

        # => Initial Guess (UCHF Result) <= #

        X = ls.Tensor.array(G)
        X.np[...] /= 4. * F.np[...]

        # ==> Iterations <== #

        converged = False
        #print 'CPHF Iterations:\n'
        #print '%4s: %11s' % ('Iter', 'dR')
        for iter in range(options['maxiter'] + 1):

            # => Compute the Residual <= #

            R = ls.Tensor.array(G)
            # One-electron term
            R.np[...] -= 4. * F.np[...] * X.np[...]
            # Two-electron terms
            D = ls.Tensor.chain(
                [self.tensors['Cocc'], X, self.tensors['Cvir']],
                [False, False, True])
            D.symmetrize()
            I = ls.Tensor.zeros_like(D)
            I.np[...] += 16. * ls.IntBox.coulomb(
                self.resources,
                ls.Ewald.coulomb(),
                self.pairlist,
                self.pairlist,
                D,
                options['thre_sp'],
                options['thre_dp'],
            ).np
            I.np[...] -= 8. * ls.IntBox.exchange(
                self.resources,
                ls.Ewald.coulomb(),
                self.pairlist,
                self.pairlist,
                D,
                True,
                options['thre_sp'],
                options['thre_dp'],
            ).np
            R.np[...] -= ls.Tensor.chain(
                [self.tensors['Cocc'], I, self.tensors['Cvir']],
                [True, False, False])

            # => Precondition the Residual <= #

            R.np[...] /= 4. * F.np
            X.np[...] += R.np  # Approximate NR-Step

            # => Check Convergence <= #

            Rmax = np.max(np.abs(R))
            #print '%4d: %11.3E' % (iter, Rmax)
            if Rmax < options['convergence']:
                converged = True
                break

            # => Perform DIIS <= #

            X = diis.iterate(X, R, use_disk=options['diis_use_disk'])

        # => Print Convergence <= #

        #print ''
        if converged:
            #print 'CPHF Converged\n'
            pass
        else:
            #print 'CPHF Failed\n'
            return False

        # => Trailer <= #

        #print '==> End CPHF <==\n'

        return X
示例#14
0
    def localize(
        self,
        C,
        F, 
        print_level=0,
        ):

        """ Localize orbitals.

        Params:
            C (ls.Tensor of shape (nao, ni)) - Orbitals to localize - these
                must live inside the span of Cref.
            F (ls.Tensor of shape, (ni, ni)) - Fock matrix in orbital basis.
                Used to sort localized orbitals to ascending orbital energies.
            print_level (int) - 0 - no printing, 1 - print iterative history
                and converence info.
        Returns:
            U (ls.Tensor of shape (ni, ni)) - Rotation from original orbitals
                (rows) to localized orbitals (cols). 
            L (ls.Tensor of shape (nao, ni)) - Localized orbital coefficients. 
            F (ls.Tensor of shape (ni, ni)) - Fock matrix in local orbital basis.
            converged (bool) - Did the localization procedure converge (True)
                or not (False).

        Definitions:
            L = C * U
            F2 = L' * F * L

        Localized orbitals are sorted to have ascending orbital energies.
        """
        
        if print_level:
            print('==> IBO Localization <==\n')
            print('IBO Convergence Options:')
            print('  Power         = %11d'   % self.options['power'])
            print('  Maxiter       = %11d'   % self.options['maxiter'])
            print('  G Convergence = %11.3E' % self.options['g_convergence'])
            print('')

        L = ls.Tensor.chain([C,self.S,self.A],[True,False,False])
        U = ls.Tensor((C.shape[1],)*2)
        
        ret = ls.Local.localize(
            self.options['power'],
            self.options['maxiter'],
            self.options['g_convergence'],
            self.minbasis,
            L,
            U,
            )
        converged = ret[-1,1] < self.options['g_convergence']

        if print_level:
            print('IBO %4s: %24s %11s %11s' % ('Iter', 'Metric', 'Delta', 'Gradient')) 
            retnp = ret.np
            Eold = 0.0
            for ind in range(ret.shape[0]):
                E = retnp[ind,0]
                G = retnp[ind,1]
                print('IBO %4d: %24.16E %11.3E %11.3E' % (
                    ind+1,
                    E,
                    E-Eold,
                    G,
                    ))
                Eold = E
            print('')
            if converged:
                print('IBO converged\n')
            else:
                print('IBO failed\n')

        # Energy ordering
        eps = np.diag(ls.Tensor.chain([U,F,U],[True,False,False]))
        U = ls.Tensor.array(U[:,np.argsort(eps)]) 
                        
        # Localized orbitals
        C2 = ls.Tensor.chain([C,U],[False,False])        

        # Localized Fock matrix
        F = ls.Tensor.chain([U,F,U],[True,False,False])

        if print_level:
            print('==> End IBO Localization <==\n')

        return U, C2, F, converged