def lanczos_minmax(F, S=None, **kwargs): "Estimate the min/max evals of F using a few iters of Lanczos" doS = S is not None niter = kwargs.get('niter', settings.DMPLanczosMinmaxIters) N = F.shape[0] niter = min(N, niter) x = zeros(N, 'd') x[0] = 1 q = x avals = [] bvals = [] if doS: r = matrixmultiply(S, q) else: r = q beta = sqrt(matrixmultiply(q, r)) wold = zeros(N, 'd') for i in xrange(niter): w = r / beta v = q / beta r = matrixmultiply(F, v) r = r - wold * beta alpha = matrixmultiply(v, r) avals.append(alpha) r = r - w * alpha if doS: q = solve(S, r) else: q = r beta = sqrt(matrixmultiply(q, r)) bvals.append(beta) wold = w E, V = eigh(tridiagmat(avals, bvals)) return min(E), max(E)
def __init__(self, solver): # Solver is a pointer to a HF or a DFT calculation that has # already converged self.solver = solver self.bfs = self.solver.bfs self.nbf = len(self.bfs) self.S = self.solver.S self.h = self.solver.h self.Ints = self.solver.Ints self.molecule = self.solver.molecule self.nel = self.molecule.get_nel() self.nclosed, self.nopen = self.molecule.get_closedopen() self.Enuke = self.molecule.get_enuke() self.norb = self.nbf self.orbs = self.solver.orbs self.orbe = self.solver.orbe self.Gij = [] for g in xrange(self.nbf): gmat = zeros((self.nbf, self.nbf), 'd') self.Gij.append(gmat) gbf = self.bfs[g] for i in xrange(self.nbf): ibf = self.bfs[i] for j in xrange(i + 1): jbf = self.bfs[j] gij = three_center(ibf, gbf, jbf) gmat[i, j] = gij gmat[j, i] = gij D0 = mkdens(self.orbs, 0, self.nclosed) J0 = getJ(self.Ints, D0) Vfa = (2.0 * (self.nel - 1.0) / self.nel) * J0 self.H0 = self.h + Vfa self.b = zeros(self.nbf, 'd') return
def lanczos_minmax(F,S=None,**kwargs): "Estimate the min/max evals of F using a few iters of Lanczos" doS = S is not None niter = kwargs.get('niter',8) N = F.shape[0] niter = min(N,niter) x = zeros(N,'d') x[0] = 1 q = x avals = [] bvals = [] if doS: r = matrixmultiply(S,q) else: r = q beta = sqrt(matrixmultiply(q,r)) wold = zeros(N,'d') for i in xrange(niter): w = r/beta v = q/beta r = matrixmultiply(F,v) r = r - wold*beta alpha = matrixmultiply(v,r) avals.append(alpha) r = r-w*alpha if doS: q = solve(S,r) else: q = r beta = sqrt(matrixmultiply(q,r)) bvals.append(beta) wold = w E,V = eigh(tridiagmat(avals,bvals)) return min(E),max(E)
def __init__(self,solver): # Solver is a pointer to a HF or a DFT calculation that has # already converged self.solver = solver self.bfs = self.solver.bfs self.nbf = len(self.bfs) self.S = self.solver.S self.h = self.solver.h self.Ints = self.solver.Ints self.molecule = self.solver.molecule self.nel = self.molecule.get_nel() self.nclosed, self.nopen = self.molecule.get_closedopen() self.Enuke = self.molecule.get_enuke() self.norb = self.nbf self.orbs = self.solver.orbs self.orbe = self.solver.orbe self.Gij = [] for g in range(self.nbf): gmat = zeros((self.nbf,self.nbf),'d') self.Gij.append(gmat) gbf = self.bfs[g] for i in range(self.nbf): ibf = self.bfs[i] for j in range(i+1): jbf = self.bfs[j] gij = three_center(ibf,gbf,jbf) gmat[i,j] = gij gmat[j,i] = gij D0 = mkdens(self.orbs,0,self.nclosed) J0 = getJ(self.Ints,D0) Vfa = (2.0*(self.nel-1.0)/self.nel)*J0 self.H0 = self.h + Vfa self.b = zeros(self.nbf,'d') return
def CPBE(dens,gamma): "PBE Correlation Functional" npts = len(dens) ec = zeros(npts,'d') vc = zeros(npts,'d') for i in xrange(npts): rho = 0.5*float(dens[i]) # Density of the alpha spin gam = 0.25*gamma[i] ecab,vca,vcb = cpbe(rho,rho,gam,gam,gam) ec[i] = ecab vc[i] = vca return ec,vc
def XPBE(dens,gamma): "PBE Exchange Functional" npts = len(dens) assert len(gamma) == npts ex = zeros(npts,'d') vx = zeros(npts,'d') for i in xrange(npts): rho = 0.5*float(dens[i]) # Density of the alpha spin gam = 0.25*gamma[i] exa,vxa = xpbe(rho,gam) ex[i] = 2*exa vx[i] = vxa return ex,vx
def getF(self, F, D): n, m = F.shape err = matrixmultiply(F,matrixmultiply(D,self.S)) -\ matrixmultiply(self.S,matrixmultiply(D,F)) err = ravel(err) maxerr = max(abs(err)) self.maxerr = maxerr if maxerr < self.errcutoff and not self.started: if VERBOSE: print "Starting DIIS: Max Err = ", maxerr self.started = 1 if not self.started: # Do simple averaging until DIIS starts if self.Fold != None: Freturn = 0.5 * F + 0.5 * self.Fold self.Fold = F else: self.Fold = F Freturn = F return Freturn self.Fs.append(F) self.Errs.append(err) nit = len(self.Errs) a = zeros((nit + 1, nit + 1), 'd') b = zeros(nit + 1, 'd') for i in xrange(nit): for j in xrange(nit): a[i, j] = dot(self.Errs[i], self.Errs[j]) for i in xrange(nit): a[nit, i] = a[i, nit] = -1.0 b[i] = 0 #mtx2file(a,'A%d.dat' % nit) a[nit, nit] = 0 b[nit] = -1.0 # The try loop makes this a bit more stable. # Thanks to John Kendrick! try: c = solve(a, b) except: self.Fold = F return F F = zeros((n, m), 'd') for i in xrange(nit): F += c[i] * self.Fs[i] return F
def getF(self, F, D): n, m = F.shape err = matrixmultiply(F, matrixmultiply(D, self.S)) - matrixmultiply(self.S, matrixmultiply(D, F)) err = ravel(err) maxerr = max(abs(err)) self.maxerr = maxerr if maxerr < self.errcutoff and not self.started: if VERBOSE: print "Starting DIIS: Max Err = ", maxerr self.started = 1 if not self.started: # Do simple averaging until DIIS starts if self.Fold != None: Freturn = 0.5 * F + 0.5 * self.Fold self.Fold = F else: self.Fold = F Freturn = F return Freturn self.Fs.append(F) self.Errs.append(err) nit = len(self.Errs) a = zeros((nit + 1, nit + 1), "d") b = zeros(nit + 1, "d") for i in range(nit): for j in range(nit): a[i, j] = dot(self.Errs[i], self.Errs[j]) for i in range(nit): a[nit, i] = a[i, nit] = -1.0 b[i] = 0 # mtx2file(a,'A%d.dat' % nit) a[nit, nit] = 0 b[nit] = -1.0 # The try loop makes this a bit more stable. # Thanks to John Kendrick! try: c = solve(a, b) except: self.Fold = F return F F = zeros((n, m), "d") for i in range(nit): F += c[i] * self.Fs[i] return F
def get1ints(bfs,atoms): "Form the overlap S and h=t+vN one-electron Hamiltonian matrices" nbf = len(bfs) S = zeros((nbf,nbf),'d') h = zeros((nbf,nbf),'d') for i in xrange(nbf): bfi = bfs[i] for j in xrange(nbf): bfj = bfs[j] S[i,j] = bfi.overlap(bfj) h[i,j] = bfi.kinetic(bfj) for atom in atoms: h[i,j] = h[i,j] + atom.Z*bfi.nuclear(bfj,atom.pos()) return S,h
def get_gradient(self, b): energy = self.get_energy(b) Fmoa = simx(self.Fa, self.orbsa) Fmob = simx(self.Fb, self.orbsb) bp = zeros(2 * self.nbf, 'd') for g in xrange(self.nbf): # Transform Gij[g] to MOs. This is done over the whole # space rather than just the parts we need. I can speed # this up later by only forming the i,a elements required Gmo = simx(self.Gij[g], self.orbsa) # Now sum the appropriate terms to get the b gradient for i in xrange(self.nalpha): for a in xrange(self.nalpha, self.norb): bp[g] += Fmoa[i, a] * Gmo[i, a] / (self.orbea[i] - self.orbea[a]) for g in xrange(self.nbf): # Transform Gij[g] to MOs. This is done over the whole # space rather than just the parts we need. I can speed # this up later by only forming the i,a elements required Gmo = simx(self.Gij[g], self.orbsb) # Now sum the appropriate terms to get the b gradient for i in xrange(self.nbeta): for a in xrange(self.nbeta, self.norb): bp[self.nbf + g] += Fmob[i, a] * Gmo[i, a] / (self.orbeb[i] - self.orbeb[a]) #logging.debug("EXX Grad: %10.5f" % (sqrt(dot(bp,bp)))) return bp
def get_rot(h,Hs,f,a,b,noccsh): nocc = sum(noccsh) nsh = len(noccsh) rot = zeros((nocc,nocc),'d') for i in xrange(nocc): ish = get_sh(i,noccsh) for j in xrange(nocc): jsh = get_sh(j,noccsh) if jsh == ish: continue Wij = -0.5*(h[i,j]+Hs[0][i,j]) Wii = -0.5*(h[i,i]+Hs[0][i,i]) Wjj = -0.5*(h[j,j]+Hs[0][j,j]) for k in xrange(nsh): Wij = Wij - 0.5*Hs[2*i+1][i,j] Wii = Wij - 0.5*Hs[2*i+1][i,i] Wjj = Wij - 0.5*Hs[2*i+1][j,j] Jij = Hs[2*jsh][i,i] Kij = Hs[2*jsh+1][i,i] gamma = Kij-0.5*(Kij+Jij) Xij = -Wij Bij = Wii-Wjj+gamma if Bij > 0: Rij = -Xij/Bij else: Rij = Xij/Bij rot[i,j] = rot[j,i] = Rij return rot
def inertial(self): "Transform to inertial coordinates" from PyQuante.NumWrap import zeros,eigh rcom = self.com() print "Translating to COM: ",rcom self.translate(-rcom) I = zeros((3,3),'d') for atom in self: m = atom.mass() x,y,z = atom.pos() x2,y2,z2 = x*x,y*y,z*z I[0,0] += m*(y2+z2) I[1,1] += m*(x2+z2) I[2,2] += m*(x2+y2) I[0,1] -= m*x*y I[1,0] = I[0,1] I[0,2] -= m*x*z I[2,0] = I[0,2] I[1,2] -= m*y*z I[2,1] = I[1,2] E,U = eigh(I) print "Moments of inertial ",E self.urotate(U) print "New coordinates: " print self return
def inertial(self): "Transform to inertial coordinates" from PyQuante.NumWrap import zeros, eigh rcom = self.com() print "Translating to COM: ", rcom self.translate(-rcom) I = zeros((3, 3), 'd') for atom in self: m = atom.mass() x, y, z = atom.pos() x2, y2, z2 = x * x, y * y, z * z I[0, 0] += m * (y2 + z2) I[1, 1] += m * (x2 + z2) I[2, 2] += m * (x2 + y2) I[0, 1] -= m * x * y I[1, 0] = I[0, 1] I[0, 2] -= m * x * z I[2, 0] = I[0, 2] I[1, 2] -= m * y * z I[2, 1] = I[1, 2] E, U = eigh(I) print "Moments of inertial ", E self.urotate(U) print "New coordinates: " print self return
def get_gradient(self,b): energy = self.get_energy(b) Fmoa = simx(self.Fa,self.orbsa) Fmob = simx(self.Fb,self.orbsb) bp = zeros(2*self.nbf,'d') for g in range(self.nbf): # Transform Gij[g] to MOs. This is done over the whole # space rather than just the parts we need. I can speed # this up later by only forming the i,a elements required Gmo = simx(self.Gij[g],self.orbsa) # Now sum the appropriate terms to get the b gradient for i in range(self.nalpha): for a in range(self.nalpha,self.norb): bp[g] += Fmoa[i,a]*Gmo[i,a]/(self.orbea[i]-self.orbea[a]) for g in range(self.nbf): # Transform Gij[g] to MOs. This is done over the whole # space rather than just the parts we need. I can speed # this up later by only forming the i,a elements required Gmo = simx(self.Gij[g],self.orbsb) # Now sum the appropriate terms to get the b gradient for i in range(self.nbeta): for a in range(self.nbeta,self.norb): bp[self.nbf+g] += Fmob[i,a]*Gmo[i,a]/(self.orbeb[i]-self.orbeb[a]) #logging.debug("EXX Grad: %10.5f" % (sqrt(dot(bp,bp)))) return bp
def fetch_kints(Ints,i,j,nbf): temp = zeros(nbf*nbf,'d') kl = 0 for k in xrange(nbf): for l in xrange(nbf): temp[kl] = Ints[intindex(i,k,j,l)] kl += 1 return temp
def tridiagmat(alpha, beta): N = len(alpha) A = zeros((N, N), 'd') for i in xrange(N): A[i, i] = alpha[i] if i < N - 1: A[i, i + 1] = A[i + 1, i] = beta[i] return A
def getF(self, F, D): n, m = F.shape err = matrixmultiply(F, matrixmultiply(D, self.S)) - matrixmultiply(self.S, matrixmultiply(D, F)) err = ravel(err) maxerr = max(abs(err)) if maxerr < self.errcutoff and not self.started: if VERBOSE: print "Starting DIIS: Max Err = ", maxerr self.started = 1 if not self.started: # Do simple averaging until DIIS starts if self.Fold: Freturn = 0.5 * F + 0.5 * self.Fold else: Freturn = F self.Fold = F return Freturn elif not self.errold: Freturn = 0.5 * F + 0.5 * self.Fold self.errold = err return Freturn a = zeros((3, 3), "d") b = zeros(3, "d") a[0, 0] = dot(self.errold, self.errold) a[1, 0] = dot(self.errold, err) a[0, 1] = a[1, 0] a[1, 1] = dot(err, err) a[:, 2] = -1 a[2, :] = -1 a[2, 2] = 0 b[2] = -1 c = solve(a, b) # Handle a few special cases: alpha = c[1] print alpha, c # if alpha < 0: alpha = 0 # if alpha > 1: alpha = 1 F = (1 - alpha) * self.Fold + alpha * F self.errold = err self.Fold = F return F
def getF(self, F, D): n, m = F.shape err = matrixmultiply(F,matrixmultiply(D,self.S)) -\ matrixmultiply(self.S,matrixmultiply(D,F)) err = ravel(err) maxerr = max(abs(err)) if maxerr < self.errcutoff and not self.started: if VERBOSE: print "Starting DIIS: Max Err = ", maxerr self.started = 1 if not self.started: # Do simple averaging until DIIS starts if self.Fold: Freturn = 0.5 * F + 0.5 * self.Fold else: Freturn = F self.Fold = F return Freturn elif not self.errold: Freturn = 0.5 * F + 0.5 * self.Fold self.errold = err return Freturn a = zeros((3, 3), 'd') b = zeros(3, 'd') a[0, 0] = dot(self.errold, self.errold) a[1, 0] = dot(self.errold, err) a[0, 1] = a[1, 0] a[1, 1] = dot(err, err) a[:, 2] = -1 a[2, :] = -1 a[2, 2] = 0 b[2] = -1 c = solve(a, b) # Handle a few special cases: alpha = c[1] print alpha, c #if alpha < 0: alpha = 0 #if alpha > 1: alpha = 1 F = (1 - alpha) * self.Fold + alpha * F self.errold = err self.Fold = F return F
def tridiagmat(alpha,beta): N = len(alpha) A = zeros((N,N),'d') for i in xrange(N): A[i,i] = alpha[i] if i < N-1: A[i,i+1] = A[i+1,i] = beta[i] return A
def appendColumn(A, newVec): """\ Append a column vector onto matrix A; this creates a new matrix and does the relevant copying. """ n, m = A.shape Anew = zeros((n, m + 1), 'd') Anew[:, :m] = A Anew[:, m] = newVec return Anew
def appendColumn(A,newVec): """\ Append a column vector onto matrix A; this creates a new matrix and does the relevant copying. """ n,m = A.shape Anew = zeros((n,m+1),'d') Anew[:,:m] = A Anew[:,m] = newVec return Anew
def com(self): "Compute the center of mass of the molecule" from PyQuante.NumWrap import zeros rcom = zeros((3,),'d') mtot = 0 for atom in self: m = atom.mass() rcom += m*atom.r mtot += m rcom /= mtot return rcom
def getV(bfs,atoms): "Form the nuclear attraction matrix V" nbf = len(bfs) V = zeros((nbf,nbf),'d') for i in xrange(nbf): bfi = bfs[i] for j in xrange(nbf): bfj = bfs[j] for atom in atoms: V[i,j] = V[i,j] + atom.atno*bfi.nuclear(bfj,atom.pos()) return V
def getS(bfs): "Form the overlap matrix" nbf = len(bfs) S = zeros((nbf,nbf),'d') for i in xrange(nbf): bfi = bfs[i] for j in xrange(nbf): bfj = bfs[j] S[i,j] = bfi.overlap(bfj) return S
def getT(bfs): "Form the kinetic energy matrix" nbf = len(bfs) T = zeros((nbf,nbf),'d') for i in xrange(nbf): bfi = bfs[i] for j in xrange(nbf): bfj = bfs[j] T[i,j] = bfi.kinetic(bfj) return T
def com(self): "Compute the center of mass of the molecule" from PyQuante.NumWrap import zeros rcom = zeros((3, ), 'd') mtot = 0 for atom in self: m = atom.mass() rcom += m * atom.r mtot += m rcom /= mtot return rcom
def get_X_C(gr,FX = 1.0, FC = 1.0,**kwargs): "Form the exchange-correlation matrix" functional = kwargs.get('functional','SVWN') gr.floor_density() # Insure that the values of the density don't underflow dens = gr.dens() weight = gr.weights() gamma = gr.get_gamma() npts = len(dens) if gr.version == 1: amdens = zeros((2,npts),'d') amgamma = zeros((3,npts),'d') amdens[0,:] = amdens[1,:] = 0.5*dens if gamma is not None: amgamma[0,:] = amgamma[1,:] = amgamma[2,:] = 0.25*gamma elif gr.version == 2: amdens = gr.density.T amgamma = gr.gamma.T #fxc,dfxcdna,dfxcdnb,dfxcdgaa,dfxcdgab,dfxcdgbb = XC(amdens,amgamma,**kwargs) fx,dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb = Exch(amdens,amgamma,**kwargs) fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb = Corr(amdens,amgamma,**kwargs) fxc = FX*fx+FC*fc dfxcdna = FX*dfxdna + FC*dfcdna Exc = dot(weight,fxc) wva = weight*dfxcdna # Combine w*v in a vector for multiplication by bfs # First do the part that doesn't depend upon gamma nbf = gr.get_nbf() Fxca = zeros((nbf,nbf),'d') for i in xrange(nbf): wva_i = wva*gr.bfgrid[:,i] for j in xrange(nbf): Fxca[i,j] = dot(wva_i,gr.bfgrid[:,j]) return Exc,Fxca
def davidson(A, nroots, **kwargs): etol = kwargs.get('etol', settings.DavidsonEvecTolerance ) # tolerance on the eigenval convergence ntol = kwargs.get('ntol', settings.DavidsonNormTolerance ) # tolerance on the vector norms for addn n, m = A.shape ninit = max(nroots, 2) B = zeros((n, ninit), 'd') for i in xrange(ninit): B[i, i] = 1. nc = 0 # number of converged roots eigold = 1e10 for iter in xrange(n): if nc >= nroots: break D = matrixmultiply(A, B) S = matrixmultiply(transpose(B), D) m = len(S) eval, evec = eigh(S) bnew = zeros(n, 'd') for i in xrange(m): bnew += evec[i, nc] * (D[:, i] - eval[nc] * B[:, i]) for i in xrange(n): denom = max(eval[nc] - A[i, i], 1e-8) # Maximum amplification factor bnew[i] /= denom norm = orthog(bnew, B) bnew = bnew / norm if abs(eval[nc] - eigold) < etol: nc += 1 eigold = eval[nc] if norm > ntol: B = appendColumn(B, bnew) E = eval[:nroots] nv = len(evec) V = matrixmultiply(B[:, :nv], evec) return E, V
def VWN(dens,gamma=None): """ Vosko-Wilk-Nusair correlation functional for vectors of spin up and spin down densities. From 'Accurate spin-dependent electron liquid correlation energies for local spin density calculations: a critical analysis.' SH Vosko, L Wilk, M Nusair, Can J Phys, 58, 1200 (1980). Note that gamma is ignored, and is only included for consistency with the other XC functionals. AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts fc = zeros(npts,'d') dfcdna = zeros(npts,'d') dfcdnb = zeros(npts,'d') dfcdgaa = zeros(npts,'d') dfcdgab = zeros(npts,'d') dfcdgbb = zeros(npts,'d') for i in xrange(npts): na = float(dens[0][i]) # Density of the alpha spin nb = float(dens[1][i]) # Density of the beta spin fcab,vca,vcb = cvwn(na,nb) fc[i] = fcab dfcdna[i] = vca dfcdnb[i] = vcb return fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb
def LYP(dens,gamma): """Transformed version of LYP. See 'Results obtained with correlation energy density functionals of Becke and Lee, Yang, and Parr.' Miehlich, Savin, Stoll and Preuss. CPL 157, 200 (1989). AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts assert len(gamma[0]) == npts assert len(gamma[1]) == npts assert len(gamma[2]) == npts fc = zeros(npts,'d') dfcdna = zeros(npts,'d') dfcdnb = zeros(npts,'d') dfcdgaa = zeros(npts,'d') dfcdgab = zeros(npts,'d') dfcdgbb = zeros(npts,'d') for i in xrange(npts): na = float(dens[0][i]) # Density of the alpha spin nb = float(dens[1][i]) # Density of the beta spin gamaa = gamma[0][i] gamab = gamma[1][i] gambb = gamma[2][i] fcab,fcna,fcnb,fcgaa,fcgab,fcgbb = clyp(na,nb,gamaa,gamab,gambb, return_flag=1) fc[i] = fcab dfcdna[i] = fcna dfcdnb[i] = fcnb dfcdgaa[i] = fcgaa dfcdgab[i] = fcgab dfcdgbb[i] = fcgbb return fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb
def S(dens,gamma=None): """ Slater exchange functional for vectors of spin-up and spin-down densities. Based upon the classic Slater exchange (which actually came from Dirac). See JC Slater 'The self consistent field for molecules and solids', McGraw Hill, New York, 1974. Note that gamma is ignored, and is only included for consistency with the other XC functionals. Also note that this exchange is written as a sum but doesn't use the spin-scaling relationship used for 'physics' exchange (LDA, PBE, PW91). AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts fx = zeros(npts,'d') dfxdna = zeros(npts,'d') dfxdnb = zeros(npts,'d') dfxdgaa = zeros(npts,'d') dfxdgab = zeros(npts,'d') dfxdgbb = zeros(npts,'d') for i in xrange(npts): na = float(dens[0][i]) # Density of the alpha spin nb = float(dens[1][i]) # Density of the beta spin fxa,vxa = xs(na) fxb,vxb = xs(nb) fx[i] = fxa + fxb dfxdna[i] = vxa dfxdnb[i] = vxb return fx,dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb
def AM05(dens,gamma): """Armiento and Mattsson functional from 2005. (note: no spin) Rickard Armiento and Ann E Mattsson, PRB 72, 085108 (2005). AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts assert len(gamma[0]) == npts assert len(gamma[1]) == npts assert len(gamma[2]) == npts fxc = zeros(npts,'d') dfxcdna = zeros(npts,'d') dfxcdnb = zeros(npts,'d') dfxcdgaa = zeros(npts,'d') dfxcdgab = zeros(npts,'d') dfxcdgbb = zeros(npts,'d') for i in xrange(npts): rho = float(dens[0][i]+dens[1][i]) # Total density gam = gamma[0][i]+gamma[2][i] + 2.0*gamma[1][i] # Total gamma fpnt,dfdrho,dfdgamma = am05xc(rho,gam) fxc[i] = fpnt dfxcdna[i] = dfdrho dfxcdnb[i] = dfdrho dfxcdgaa[i] = dfdgamma dfxcdgab[i] = 2.0*dfdgamma dfxcdgbb[i] = dfdgamma return fxc,dfxcdna,dfxcdnb,dfxcdgaa,dfxcdgab,dfxcdgbb
def PW(dens,gamma=None): """ Perdew-Wang correlation functional for vectors of spin up and spin down densities. From 'Accurate and simple analytical representation of the electron-gas correlation energy' John P. Perdew and Yue Wang, Phys. Rev. B 45, 13244 (1992). Note that gamma is ignored, and is only included for consistency with the other XC functionals. AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts fc = zeros(npts,'d') dfcdna = zeros(npts,'d') dfcdnb = zeros(npts,'d') dfcdgaa = zeros(npts,'d') dfcdgab = zeros(npts,'d') dfcdgbb = zeros(npts,'d') for i in xrange(npts): na = float(dens[0][i]) # Density of the alpha spin nb = float(dens[1][i]) # Density of the beta spin fcab,vca,vcb = pw(na,nb) fc[i] = fcab dfcdna[i] = vca dfcdnb[i] = vcb return fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb
def B(dens,gamma): """ Becke 1988 Exchange Functional. From 'Density-functional exchange energy approximation with correct asymptotic behavior.' AD Becke, PRA 38, 3098 (1988). Chemistry way. AEM June 2006. """ npts = len(dens[0]) assert len(dens[1]) == npts assert len(gamma[0]) == npts assert len(gamma[1]) == npts assert len(gamma[2]) == npts fx = zeros(npts,'d') dfxdna = zeros(npts,'d') dfxdnb = zeros(npts,'d') dfxdgaa = zeros(npts,'d') dfxdgab = zeros(npts,'d') dfxdgbb = zeros(npts,'d') for i in xrange(npts): na = float(dens[0][i]) # Density of the alpha spin nb = float(dens[1][i]) # Density of the beta spin gamaa = gamma[0][i] gambb = gamma[2][i] fxa,fxna,fxgaa = xb(na,gamaa,return_flag = 1) fxb,fxnb,fxgbb = xb(nb,gambb,return_flag = 1) fx[i] = fxa + fxb dfxdna[i] = fxna dfxdnb[i] = fxnb dfxdgaa[i] = fxgaa dfxdgbb[i] = fxgbb return fx,dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb
def XC(dens,gamma,**opts): """\ New top level routine for all XC functionals. dens is a two x npts component matrix with spin-up and spin-down densities. gamma is a three x npts component matrix with grad(n1)*grad(n2) with n1 and n2 being (spin-up, spin-up), (spin-up, spin-down), and (spin-down,spin-down) densities. Should contain the right number of elements (for example zeros) even if gradients are not needed. For non-spin calculations set spin-up density = spin-down density = density/2 and gammaupup=gammaupdown=gammadowndown = |grad(rho)|**2/4. For non-spin we should get dfxcdna=dfxcdnb and dfxcdgaa=dfxcdgbb. dfxdgab = 0 always, it's included for consistency with dfcdgab. AEM June 2006. """ functional = opts.get('functional','SVWN') derivative = opts.get('derivative','analyt') assert functional in xfuncs.keys() and functional in cfuncs.keys() #that npts is the same for all 5 vectors should be checked elsewhere npts = len(dens[0]) fxc = zeros(npts,'d') dfxcdna = zeros(npts,'d') dfxcdnb = zeros(npts,'d') dfxcdgaa = zeros(npts,'d') dfxcdgab = zeros(npts,'d') dfxcdgbb = zeros(npts,'d') if xfuncs[functional]: if derivative == 'analyt' and analyt[functional]: fx,dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb = \ xfuncs[functional](dens,gamma) else: fx,dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb = \ xfuncs[functional](dens,gamma) dfxdna,dfxdnb,dfxdgaa,dfxdgab,dfxdgbb = \ numder('x',functional,dens,gamma) fxc = fxc + fx dfxcdna = dfxcdna + dfxdna dfxcdnb = dfxcdnb + dfxdnb dfxcdgaa = dfxcdgaa + dfxdgaa dfxcdgab = dfxcdgab + dfxdgab dfxcdgbb = dfxcdgbb + dfxdgbb if cfuncs[functional]: if derivative == 'analyt' and analyt[functional]: fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb = \ cfuncs[functional](dens,gamma) else: fc,dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb = \ cfuncs[functional](dens,gamma) dfcdna,dfcdnb,dfcdgaa,dfcdgab,dfcdgbb = \ numder('c',functional,dens,gamma) fxc = fxc + fc dfxcdna = dfxcdna + dfcdna dfxcdnb = dfxcdnb + dfcdnb dfxcdgaa = dfxcdgaa + dfcdgaa dfxcdgab = dfxcdgab + dfcdgab dfxcdgbb = dfxcdgbb + dfcdgbb return fxc,dfxcdna,dfxcdnb,dfxcdgaa,dfxcdgab,dfxcdgbb
def get_exx_gradient(b, nbf, nel, nocc, ETemp, Enuke, S, h, Ints, H0, Gij, **opts): """Computes the gradient for the OEP/HF functional. return_flag 0 Just return gradient 1 Return energy,gradient 2 Return energy,gradient,orbe,orbs """ # Dump the gradient every 10 steps so we can restart... global gradcall gradcall += 1 #if gradcall % 5 == 0: logging.debug("B vector:\n%s" % b) # Form the new potential and the new orbitals energy, orbe, orbs, F = get_exx_energy(b, nbf, nel, nocc, ETemp, Enuke, S, h, Ints, H0, Gij, return_flag=2) Fmo = matrixmultiply(transpose(orbs), matrixmultiply(F, orbs)) norb = nbf bp = zeros(nbf, 'd') # dE/db for g in xrange(nbf): # Transform Gij[g] to MOs. This is done over the whole # space rather than just the parts we need. I can speed # this up later by only forming the i,a elements required Gmo = matrixmultiply(transpose(orbs), matrixmultiply(Gij[g], orbs)) # Now sum the appropriate terms to get the b gradient for i in xrange(nocc): for a in xrange(nocc, norb): bp[g] = bp[g] + Fmo[i, a] * Gmo[i, a] / (orbe[i] - orbe[a]) #logging.debug("EXX Grad: %10.5f" % (sqrt(dot(bp,bp)))) return_flag = opts.get('return_flag', 0) if return_flag == 1: return energy, bp elif return_flag == 2: return energy, bp, orbe, orbs return bp
def getJ(Ints,D): "Form the Coulomb operator corresponding to a density matrix D" nbf = D.shape[0] D1d = reshape(D,(nbf*nbf,)) #1D version of Dens J = zeros((nbf,nbf),'d') for i in xrange(nbf): for j in xrange(i+1): if sorted: temp = jints[i,j] else: temp = fetch_jints(Ints,i,j,nbf) J[i,j] = dot(temp,D1d) J[j,i] = J[i,j] return J
def getK(Ints,D): "Form the exchange operator corresponding to a density matrix D" nbf = D.shape[0] D1d = reshape(D,(nbf*nbf,)) #1D version of Dens K = zeros((nbf,nbf),'d') for i in xrange(nbf): for j in xrange(i+1): if sorted: temp = kints[i,j] else: temp = fetch_kints(Ints,i,j,nbf) K[i,j] = dot(temp,D1d) K[j,i] = K[i,j] return K
def get2JmK(Ints,D): "Form the 2J-K integrals corresponding to a density matrix D" nbf = D.shape[0] D1d = reshape(D,(nbf*nbf,)) #1D version of Dens G = zeros((nbf,nbf),'d') for i in xrange(nbf): for j in xrange(i+1): if sorted: temp = 2*jints[i,j]-kints[i,j] else: temp = 2*fetch_jints(Ints,i,j,nbf)-fetch_kints(Ints,i,j,nbf) G[i,j] = dot(temp,D1d) G[j,i] = G[i,j] return G
def __init__(self, solver): # Solver is a pointer to a UHF calculation that has # already converged self.solver = solver self.bfs = self.solver.bfs self.nbf = len(self.bfs) self.S = self.solver.S self.h = self.solver.h self.Ints = self.solver.Ints self.molecule = self.solver.molecule self.nel = self.molecule.get_nel() self.nalpha, self.nbeta = self.molecule.get_alphabeta() self.Enuke = self.molecule.get_enuke() self.norb = self.nbf self.orbsa = self.solver.orbsa self.orbsb = self.solver.orbsb self.orbea = self.solver.orbea self.orbeb = self.solver.orbeb self.Gij = [] for g in xrange(self.nbf): gmat = zeros((self.nbf, self.nbf), 'd') self.Gij.append(gmat) gbf = self.bfs[g] for i in xrange(self.nbf): ibf = self.bfs[i] for j in xrange(i + 1): jbf = self.bfs[j] gij = three_center(ibf, gbf, jbf) gmat[i, j] = gij gmat[j, i] = gij D0 = mkdens(self.orbsa, 0, self.nalpha) + mkdens( self.orbsb, 0, self.nbeta) J0 = getJ(self.Ints, D0) Vfa = ((self.nel - 1.) / self.nel) * J0 self.H0 = self.h + Vfa self.b = zeros(2 * self.nbf, 'd') return
def davidson(A,nroots,**kwargs): etol = kwargs.get('etol',settings.DavidsonEvecTolerance) # tolerance on the eigenval convergence ntol = kwargs.get('ntol',settings.DavidsonNormTolerance) # tolerance on the vector norms for addn n,m = A.shape ninit = max(nroots,2) B = zeros((n,ninit),'d') for i in xrange(ninit): B[i,i] = 1. nc = 0 # number of converged roots eigold = 1e10 for iter in xrange(n): if nc >= nroots: break D = matrixmultiply(A,B) S = matrixmultiply(transpose(B),D) m = len(S) eval,evec = eigh(S) bnew = zeros(n,'d') for i in xrange(m): bnew += evec[i,nc]*(D[:,i] - eval[nc]*B[:,i]) for i in xrange(n): denom = max(eval[nc]-A[i,i],1e-8) # Maximum amplification factor bnew[i] /= denom norm = orthog(bnew,B) bnew = bnew / norm if abs(eval[nc]-eigold) < etol: nc += 1 eigold = eval[nc] if norm > ntol: B = appendColumn(B,bnew) E = eval[:nroots] nv = len(evec) V = matrixmultiply(B[:,:nv],evec) return E,V
def parse_orbs(lines,nbf): sympat = re.compile('^Sym\s*=\s*(\S+)\s*$') enepat = re.compile('^Ene\s*=\s*(\S+)$') spinpat = re.compile('^Spin\s*=\s*(\S+)$') occpat = re.compile('^Occup\s*=\s*(\S+)$') coefpat = re.compile('\s*(\d+)\s*(\S+)$') orbs = [] sym,ene,spin,occ,coefs = None,None,None,None,[] for line in lines: if sympat.search(line): if sym: orbs.append((sym,ene,spin,occ,coefs)) sym,ene,spin,occ,coefs = None,None,None,None,[] sym = sympat.search(line).groups()[0] elif enepat.search(line): ene = float(enepat.search(line).groups()[0]) elif spinpat.search(line): spin = spinpat.search(line).groups()[0] elif occpat.search(line): occ = float(occpat.search(line).groups()[0]) elif coefpat.search(line): a,b = coefpat.search(line).groups() iorb,coef = int(a),float(b) coefs.append((iorb,coef)) #print_orbs(orbs) norb = len(orbs) orbmtx = zeros((nbf,norb),'d') orbe = zeros(norb,'d') occs = zeros(norb,'d') for i in xrange(norb): sym,ene,spin,occ,coefs = orbs[i] occs[i] = occ orbe[i] = ene for ibf,coef in coefs: orbmtx[ibf-1,i] = coef return occs,orbe,orbmtx
def get_fab(nclosed,nopen): f = [] iopen_start = 0 if nclosed: f.append(1.0) iopen_start = 1 if nopen: f.append(0.5) nsh = len(f) a = zeros((nsh,nsh),'d') b = zeros((nsh,nsh),'d') for i in xrange(nsh): for j in xrange(nsh): a[i,j] = 2.*f[i]*f[j] b[i,j] = -f[i]*f[j] if nopen == 1: a[iopen_start,iopen_start] = 0 b[iopen_start,iopen_start] = 0 elif nopen > 1: b[iopen_start,iopen_start] = -0.5 return f,a,b
def ocbse(orbs,h,Hs,f,a,b,noccsh): # Need to write this so that we don't need the orbs 3 times! nsh = len(noccsh) nbf = norb = h.shape[0] orbe = zeros(norb,'d') for ish in xrange(nsh): orbs_in_shell = get_orbs_in_shell(ish,noccsh,norb) F = get_os_fock(ish,nsh,f,a,b,h,Hs) # form the orbital space of all of the orbs in ish plus the virts T = orbs.take(orbs_in_shell,1) #print "take worked? ",(T==get_orbs(orbs,orbs_in_shell)).all() # Transform to MO space Fmo = ao2mo(F,T) mo_orbe,mo_orbs = eigh(Fmo) T = matrixmultiply(T,mo_orbs) # Insert orbital energies into the right place update_orbe(orbs_in_shell,orbe,mo_orbe) update_orbs(orbs_in_shell,orbs,T) return orbe,orbs
def jacobi(A, **kwargs): """\ E,V = jacobi(A,**kwargs) - Solve the eigenvalues/vectors of matrix A using Jacobi's method. Options: Name Default Definition tol 1e-10 The tolerance for an element to be declared zero max_sweeps 200 Maximum number of sweeps through the matrix """ max_sweeps = kwargs.get('max_sweeps', settings.JacobiSweeps) tol = kwargs.get('tol', settings.JacobiTolerance) n = len(A) V = identity(n, 'd') b = diagonal(A) d = diagonal(A) z = zeros(n, 'd') nrot = 0 for irot in xrange(max_sweeps): sm = 0 for ip in xrange(n - 1): for iq in xrange(ip + 1, n): sm += abs(A[ip, iq]) if sm < tol: # Normal return return evsort(b, V) thresh = 0 if irot < 3: thresh = 0.2 * sm / n / n for ip in xrange(n - 1): for iq in xrange(ip + 1, n): g = 100 * abs(A[ip, iq]) if irot > 3 and g < tol: A[ip, iq] = 0 elif abs(A[ip, iq]) > thresh: h = d[iq] - d[ip] if g < tol: t = A[ip, iq] / h # t = 1/(2\theta) else: theta = 0.5 * h / A[ip, iq] # eq 11.1.10 t = 1. / (abs(theta) + sqrt(1. + theta * theta)) if theta < 0: t = -t c = 1.0 / sqrt(1 + t * t) s = t * c tau = s / (1.0 + c) h = t * A[ip, iq] z[ip] -= h z[iq] += h d[ip] -= h d[iq] += h A[ip, iq] = 0 for j in xrange(ip): A[j, ip], A[j, iq] = rotate(A[j, ip], A[j, iq], s, tau) for j in xrange(ip + 1, iq): A[ip, j], A[j, iq] = rotate(A[ip, j], A[j, iq], s, tau) for j in xrange(iq + 1, n): A[ip, j], A[iq, j] = rotate(A[ip, j], A[iq, j], s, tau) for j in xrange(n): V[j, ip], V[j, iq] = rotate(V[j, ip], V[j, iq], s, tau) nrot += 1 for ip in xrange(n): b[ip] += z[ip] d[ip] = b[ip] z[ip] = 0 else: print "Too many iterations" return None
def oep(atoms, orbs, energy_func, grad_func=None, **opts): """oep - Form the optimized effective potential for a given energy expression oep(atoms,orbs,energy_func,grad_func=None,**opts) atoms A Molecule object containing a list of the atoms orbs A matrix of guess orbitals energy_func The function that returns the energy for the given method grad_func The function that returns the force for the given method Options ------- verbose False Output terse information to stdout (default) True Print out additional information ETemp False Use ETemp value for finite temperature DFT (default) float Use (float) for the electron temperature bfs None The basis functions to use. List of CGBF's basis_data None The basis data to use to construct bfs integrals None The one- and two-electron integrals to use If not None, S,h,Ints """ verbose = opts.get('verbose', False) ETemp = opts.get('ETemp', False) opt_method = opts.get('opt_method', 'BFGS') bfs = opts.get('bfs', None) if not bfs: basis = opts.get('basis', None) bfs = getbasis(atoms, basis) # The basis set for the potential can be set different from # that used for the wave function pbfs = opts.get('pbfs', None) if not pbfs: pbfs = bfs npbf = len(pbfs) integrals = opts.get('integrals', None) if integrals: S, h, Ints = integrals else: S, h, Ints = getints(bfs, atoms) nel = atoms.get_nel() nocc, nopen = atoms.get_closedopen() Enuke = atoms.get_enuke() # Form the OEP using Yang/Wu, PRL 89 143002 (2002) nbf = len(bfs) norb = nbf bp = zeros(nbf, 'd') bvec = opts.get('bvec', None) if bvec: assert len(bvec) == npbf b = array(bvec) else: b = zeros(npbf, 'd') # Form and store all of the three-center integrals # we're going to need. # These are <ibf|gbf|jbf> (where 'bf' indicates basis func, # as opposed to MO) # N^3 storage -- obviously you don't want to do this for # very large systems Gij = [] for g in xrange(npbf): gmat = zeros((nbf, nbf), 'd') Gij.append(gmat) gbf = pbfs[g] for i in xrange(nbf): ibf = bfs[i] for j in xrange(i + 1): jbf = bfs[j] gij = three_center(ibf, gbf, jbf) gmat[i, j] = gij gmat[j, i] = gij # Compute the Fermi-Amaldi potential based on the LDA density. # We're going to form this matrix from the Coulombic matrix that # arises from the input orbitals. D0 and J0 refer to the density # matrix and corresponding Coulomb matrix D0 = mkdens(orbs, 0, nocc) J0 = getJ(Ints, D0) Vfa = (2 * (nel - 1.) / nel) * J0 H0 = h + Vfa b = fminBFGS(energy_func, b, grad_func, (nbf, nel, nocc, ETemp, Enuke, S, h, Ints, H0, Gij), logger=logging) energy, orbe, orbs = energy_func(b, nbf, nel, nocc, ETemp, Enuke, S, h, Ints, H0, Gij, return_flag=1) return energy, orbe, orbs
def oep_hf_an(atoms, orbs, **opts): """oep_hf - Form the optimized effective potential for HF exchange. Implementation of Wu and Yang's Approximate Newton Scheme from J. Theor. Comp. Chem. 2, 627 (2003). oep_hf(atoms,orbs,**opts) atoms A Molecule object containing a list of the atoms orbs A matrix of guess orbitals Options ------- bfs None The basis functions to use for the wfn pbfs None The basis functions to use for the pot basis_data None The basis data to use to construct bfs integrals None The one- and two-electron integrals to use If not None, S,h,Ints """ maxiter = opts.get('maxiter', 100) tol = opts.get('tol', 1e-5) bfs = opts.get('bfs', None) if not bfs: basis = opts.get('basis', None) bfs = getbasis(atoms, basis) # The basis set for the potential can be set different from # that used for the wave function pbfs = opts.get('pbfs', None) if not pbfs: pbfs = bfs npbf = len(pbfs) integrals = opts.get('integrals', None) if integrals: S, h, Ints = integrals else: S, h, Ints = getints(bfs, atoms) nel = atoms.get_nel() nocc, nopen = atoms.get_closedopen() Enuke = atoms.get_enuke() # Form the OEP using Yang/Wu, PRL 89 143002 (2002) nbf = len(bfs) norb = nbf bp = zeros(nbf, 'd') bvec = opts.get('bvec', None) if bvec: assert len(bvec) == npbf b = array(bvec) else: b = zeros(npbf, 'd') # Form and store all of the three-center integrals # we're going to need. # These are <ibf|gbf|jbf> (where 'bf' indicates basis func, # as opposed to MO) # N^3 storage -- obviously you don't want to do this for # very large systems Gij = [] for g in xrange(npbf): gmat = zeros((nbf, nbf), 'd') Gij.append(gmat) gbf = pbfs[g] for i in xrange(nbf): ibf = bfs[i] for j in xrange(i + 1): jbf = bfs[j] gij = three_center(ibf, gbf, jbf) gmat[i, j] = gij gmat[j, i] = gij # Compute the Fermi-Amaldi potential based on the LDA density. # We're going to form this matrix from the Coulombic matrix that # arises from the input orbitals. D0 and J0 refer to the density # matrix and corresponding Coulomb matrix D0 = mkdens(orbs, 0, nocc) J0 = getJ(Ints, D0) Vfa = (2 * (nel - 1.) / nel) * J0 H0 = h + Vfa b = zeros(nbf, 'd') eold = 0 for iter in xrange(maxiter): Hoep = get_Hoep(b, H0, Gij) orbe, orbs = geigh(Hoep, S) D = mkdens(orbs, 0, nocc) Vhf = get2JmK(Ints, D) energy = trace2(2 * h + Vhf, D) + Enuke if abs(energy - eold) < tol: break else: eold = energy logging.debug("OEP AN Opt: %d %f" % (iter, energy)) dV_ao = Vhf - Vfa dV = matrixmultiply(transpose(orbs), matrixmultiply(dV_ao, orbs)) X = zeros((nbf, nbf), 'd') c = zeros(nbf, 'd') Gkt = zeros((nbf, nbf), 'd') for k in xrange(nbf): # This didn't work; in fact, it made things worse: Gk = matrixmultiply(transpose(orbs), matrixmultiply(Gij[k], orbs)) for i in xrange(nocc): for a in xrange(nocc, norb): c[k] += dV[i, a] * Gk[i, a] / (orbe[i] - orbe[a]) for l in xrange(nbf): Gl = matrixmultiply(transpose(orbs), matrixmultiply(Gij[l], orbs)) for i in xrange(nocc): for a in xrange(nocc, norb): X[k, l] += Gk[i, a] * Gl[i, a] / (orbe[i] - orbe[a]) # This should actually be a pseudoinverse... b = solve(X, c) logger.info("Final OEP energy = %f" % energy) return energy, orbe, orbs
def oep_uhf_an(atoms, orbsa, orbsb, **opts): """oep_hf - Form the optimized effective potential for HF exchange. Implementation of Wu and Yang's Approximate Newton Scheme from J. Theor. Comp. Chem. 2, 627 (2003). oep_uhf(atoms,orbs,**opts) atoms A Molecule object containing a list of the atoms orbs A matrix of guess orbitals Options ------- bfs None The basis functions to use for the wfn pbfs None The basis functions to use for the pot basis_data None The basis data to use to construct bfs integrals None The one- and two-electron integrals to use If not None, S,h,Ints """ maxiter = opts.get('maxiter', 100) tol = opts.get('tol', 1e-5) ETemp = opts.get('ETemp', False) bfs = opts.get('bfs', None) if not bfs: basis = opts.get('basis', None) bfs = getbasis(atoms, basis) # The basis set for the potential can be set different from # that used for the wave function pbfs = opts.get('pbfs', None) if not pbfs: pbfs = bfs npbf = len(pbfs) integrals = opts.get('integrals', None) if integrals: S, h, Ints = integrals else: S, h, Ints = getints(bfs, atoms) nel = atoms.get_nel() nclosed, nopen = atoms.get_closedopen() nalpha, nbeta = nclosed + nopen, nclosed Enuke = atoms.get_enuke() # Form the OEP using Yang/Wu, PRL 89 143002 (2002) nbf = len(bfs) norb = nbf ba = zeros(npbf, 'd') bb = zeros(npbf, 'd') # Form and store all of the three-center integrals # we're going to need. # These are <ibf|gbf|jbf> (where 'bf' indicates basis func, # as opposed to MO) # N^3 storage -- obviously you don't want to do this for # very large systems Gij = [] for g in xrange(npbf): gmat = zeros((nbf, nbf), 'd') Gij.append(gmat) gbf = pbfs[g] for i in xrange(nbf): ibf = bfs[i] for j in xrange(i + 1): jbf = bfs[j] gij = three_center(ibf, gbf, jbf) gmat[i, j] = gij gmat[j, i] = gij # Compute the Fermi-Amaldi potential based on the LDA density. # We're going to form this matrix from the Coulombic matrix that # arises from the input orbitals. D0 and J0 refer to the density # matrix and corresponding Coulomb matrix D0 = mkdens(orbsa, 0, nalpha) + mkdens(orbsb, 0, nbeta) J0 = getJ(Ints, D0) Vfa = ((nel - 1.) / nel) * J0 H0 = h + Vfa eold = 0 for iter in xrange(maxiter): Hoepa = get_Hoep(ba, H0, Gij) Hoepb = get_Hoep(ba, H0, Gij) orbea, orbsa = geigh(Hoepa, S) orbeb, orbsb = geigh(Hoepb, S) if ETemp: efermia = get_efermi(2 * nalpha, orbea, ETemp) occsa = get_fermi_occs(efermia, orbea, ETemp) Da = mkdens_occs(orbsa, occsa) efermib = get_efermi(2 * nbeta, orbeb, ETemp) occsb = get_fermi_occs(efermib, orbeb, ETemp) Db = mkdens_occs(orbsb, occsb) entropy = 0.5 * (get_entropy(occsa, ETemp) + get_entropy(occsb, ETemp)) else: Da = mkdens(orbsa, 0, nalpha) Db = mkdens(orbsb, 0, nbeta) J = getJ(Ints, Da) + getJ(Ints, Db) Ka = getK(Ints, Da) Kb = getK(Ints, Db) energy = (trace2(2*h+J-Ka,Da)+trace2(2*h+J-Kb,Db))/2\ +Enuke if ETemp: energy += entropy if abs(energy - eold) < tol: break else: eold = energy logging.debug("OEP AN Opt: %d %f" % (iter, energy)) # Do alpha and beta separately # Alphas dV_ao = J - Ka - Vfa dV = matrixmultiply(orbsa, matrixmultiply(dV_ao, transpose(orbsa))) X = zeros((nbf, nbf), 'd') c = zeros(nbf, 'd') for k in xrange(nbf): Gk = matrixmultiply(orbsa, matrixmultiply(Gij[k], transpose(orbsa))) for i in xrange(nalpha): for a in xrange(nalpha, norb): c[k] += dV[i, a] * Gk[i, a] / (orbea[i] - orbea[a]) for l in xrange(nbf): Gl = matrixmultiply(orbsa, matrixmultiply(Gij[l], transpose(orbsa))) for i in xrange(nalpha): for a in xrange(nalpha, norb): X[k, l] += Gk[i, a] * Gl[i, a] / (orbea[i] - orbea[a]) # This should actually be a pseudoinverse... ba = solve(X, c) # Betas dV_ao = J - Kb - Vfa dV = matrixmultiply(orbsb, matrixmultiply(dV_ao, transpose(orbsb))) X = zeros((nbf, nbf), 'd') c = zeros(nbf, 'd') for k in xrange(nbf): Gk = matrixmultiply(orbsb, matrixmultiply(Gij[k], transpose(orbsb))) for i in xrange(nbeta): for a in xrange(nbeta, norb): c[k] += dV[i, a] * Gk[i, a] / (orbeb[i] - orbeb[a]) for l in xrange(nbf): Gl = matrixmultiply(orbsb, matrixmultiply(Gij[l], transpose(orbsb))) for i in xrange(nbeta): for a in xrange(nbeta, norb): X[k, l] += Gk[i, a] * Gl[i, a] / (orbeb[i] - orbeb[a]) # This should actually be a pseudoinverse... bb = solve(X, c) logger.info("Final OEP energy = %f" % energy) return energy, (orbea, orbeb), (orbsa, orbsb)
def get1ints_mpi(bfs, atoms, rank, comm, _debug_=False): "Form the overlap S and h=t+vN one-electron Hamiltonian matrices" nbf = len(bfs) # determine which orbitals we will compute npp = nbf / comm.size if rank < comm.size - 1: terms = range(rank * npp, (rank + 1) * npp) else: terms = range(rank * npp, nbf) nterm = len(terms) S = zeros((nterm, nbf)) h = zeros((nterm, nbf)) # print for debugging if _debug_: comm.Barrier() if rank == 0: print '\t Computing the 1electron integrals on %d procs' % comm.size print '\t There is %d basis functions in the system' % (len(bfs)) print '\t', print '-' * 50 print '\t [%03d] computes %d terms ' % (rank, nterm) # Compute the terms ilocal = 0 for i in terms: bfi = bfs[i] for j in xrange(nbf): bfj = bfs[j] S[ilocal, j] = bfi.overlap(bfj) h[ilocal, j] = bfi.kinetic(bfj) for atom in atoms: h[ilocal, j] = h[ilocal, j] + atom.Z * bfi.nuclear(bfj, atom.pos()) ilocal += 1 ########################################## # Start communication between nodes # to gather the total matrix on 0 ########################################## # wait for everyone to be here comm.barrier() # Create the receive buffers # only rank = 0 will receive if rank == 0: Stot = np.zeros((nbf, nbf)) htot = np.zeros((nbf, nbf)) Stot[0:npp, :] = S htot[0:npp] = h else: htot = None Stot = None if rank == 0 and _debug_: print '\n\t Procs 0 Gather all 1electron matrix' # exchange the 1 electron matrix if rank == 0: for iC in range(1, comm.size): if iC < comm.size - 1: recvbuff = np.zeros((npp, nbf)) comm.Recv(recvbuff, source=iC) htot[npp * iC:npp * (iC + 1), :] = recvbuff if _debug_: print '\t\t\t Proc [%03d] : data received from [%03d]' % ( 0, iC) else: recvbuff = np.zeros((nbf - iC * npp, nbf)) comm.Recv(recvbuff, source=iC) htot[npp * iC:, :] = recvbuff if _debug_: print '\t\t\t Proc [%03d] : data received from [%03d]' % ( 0, iC) else: comm.Send(h, dest=0) comm.Barrier() if rank == 0 and _debug_: print '\n\t Procs 0 Gather all overlap matrix' # exchange the overlap matrix if rank == 0: for iC in range(1, comm.size): if iC < comm.size - 1: recvbuff = np.zeros((npp, nbf)) comm.Recv(recvbuff, source=iC) Stot[npp * iC:npp * (iC + 1), :] = recvbuff if _debug_: print '\t\t\t Proc [%03d] : data received from [%03d]' % ( 0, iC) else: recvbuff = np.zeros((nbf - iC * npp, nbf)) comm.Recv(recvbuff, source=iC) Stot[npp * iC:, :] = recvbuff if _debug_: print '\t\t\t Proc [%03d] : data received from [%03d]' % ( 0, iC) else: comm.Send(S, dest=0) # done return Stot, htot
def get_orbs(orbs,orbs_in_shell): "This should do the same thing as take(orbs,orbs_in_shell,1)" A = zeros((orbs.shape[0],len(orbs_in_shell)),'d') for i,iorb in enumerate(orbs_in_shell): A[:,i] = orbs[:,iorb] return A