def __init__(self, symbol, configuration = None, xc = LDA, maxiter = 200, mixing = 0.4, tol = 1e-6, output = None, write = None): """ DFT atomic calculation. Parameters: symbol chemical symbol configuration '[He] 2s2 2p2', overrides default from table xc XC functional (LDA default) """ # store parameters self.element = Element(symbol) if configuration == None: self.configuration = self.element.get_configuration() else: self.configuration = configuration self.xc = xc self.maxiter = maxiter self.mixing = mixing self.tol = tol self.write = write self.set_output(output) # initialize grid, orbitals, energies self.grid = RadialGrid(self.element.get_atomic_number()) self.setup_orbitals() # print header self.print_header()
class DFTAtom: def __init__(self, symbol, configuration = None, xc = LDA, maxiter = 200, mixing = 0.4, tol = 1e-6, output = None, write = None): """ DFT atomic calculation. Parameters: symbol chemical symbol configuration '[He] 2s2 2p2', overrides default from table xc XC functional (LDA default) """ # store parameters self.element = Element(symbol) if configuration == None: self.configuration = self.element.get_configuration() else: self.configuration = configuration self.xc = xc self.maxiter = maxiter self.mixing = mixing self.tol = tol self.write = write self.set_output(output) # initialize grid, orbitals, energies self.grid = RadialGrid(self.element.get_atomic_number()) self.setup_orbitals() # print header self.print_header() def setup_orbitals(self): """Initialize atomic orbitals""" self.config_tuple = parse_configuration(self.configuration) self.orbitals = [] self.nelec = 0.0 for orb in self.config_tuple: self.orbitals.append(Orbital(orb[0], orb[1], orb[2], self.grid.npoints)) self.nelec += orb[2] def set_output(self, output): """Open output file""" if output == None: self.out = open(os.devnull, 'w') elif output == '-': self.out = sys.stdout else: self.out = open(output, 'a') def print_header(self): """Print header information""" print >> self.out, '='*78 print >> self.out, "DFT atomic calculation: Z = %i, name = %s" % (self.element.get_atomic_number(), self.element.get_name()) print >> self.out, '='*78 print >> self.out, "Radial grid : rmin = %f, rmax = %f, npoints = %i" % (self.grid.rmin, self.grid.rmax, self.grid.npoints) print >> self.out, "Atomic configuration :", tuple_to_configuration(self.config_tuple) print >> self.out, "Number of electrons :", self.nelec print >> self.out, "Exchange-correlation :", self.xc.full_name print >> self.out, "SCF : maxiter = %i, mixing = %f, tol = %f" % (self.maxiter, self.mixing, self.tol) print >> self.out, "" def print_eigenvalues(self): """Print eigenvalues and occupations""" print >> self.out, "Eigenvalues:" print >> self.out, " n l occ energy (Ha) energy (eV)" for orb in self.orbitals: print >> self.out, " %i %s %-6g %12.6f %12.6f" % (orb.n, orb.l, orb.occ, orb.ene, orb.ene*27.211383) print >> self.out, "" def print_energies(self): """Print final energies""" print >> self.out, "Total energy : %12.4f Ha" % (self.e_tot) print >> self.out, "Kinetic energy : %12.4f Ha" % (self.e_kin) print >> self.out, "Ionic energy : %12.4f Ha" % (self.e_ion) print >> self.out, "Hartree energy : %12.4f Ha" % (self.e_h) print >> self.out, "XC energy : %12.4f Ha" % (self.e_xc) def run_scf(self): """Run the SCF calculation""" # initialize potential (pure coulomb) npoints = self.grid.npoints self.v_ion = -self.grid.zeta/self.grid.r self.v_pot = self.v_ion.copy() # start SCF iteration rho_old = numpy.zeros(npoints) e_old = 0.0 for it in xrange(1, self.maxiter): # solve hydrogenoic problem for every orbital for orb in self.orbitals: ene, psi = solve_atom(self.grid, orb.n, orb.l, vpot = self.v_pot, maxiter = 50, tol = self.tol) if ene != None: orb.ene = ene orb.psi = psi.copy() # rho mixing self.calculate_rho() if it > 1: self.rho = self.mixing*self.rho + (1.0 - self.mixing)*self.rho_old self.rho_old = self.rho.copy() # calculate potential self.calculate_v_of_rho() self.v_pot = self.v_ion + self.v_h + self.v_xc # calculate energy self.e_band = 0.0 for orb in self.orbitals: self.e_band += orb.occ * orb.ene self.e_tot = self.e_band - self.e_h + self.e_xc - self.e_vxc self.e_kin = self.e_tot - self.e_ion - self.e_h - self.e_xc de_tot = self.e_tot - e_old e_old = self.e_tot if it == 1: print >> self.out, "Iteration Total energy Delta Energy" print >> self.out, "%6i %12.6f %12.4e" % (it, self.e_tot, de_tot) # check convergence and exit if abs(de_tot) < self.tol: break # end of SCF, print energy and eigenvalues print >> self.out, "" self.print_energies() self.print_eigenvalues() def calculate_rho(self): """Calculate the charge density""" self.rho = numpy.zeros(self.grid.npoints) for orb in self.orbitals: self.rho += orb.occ * (orb.psi*orb.psi) / self.grid.r2 nelec = self.grid.integrate_two(self.rho, self.grid.r2) if abs(nelec - self.nelec) > 1e-4: print >> self.out, "integrated charge (%f) different from number of electrons (%f)" % (nelec, self.nelec) self.rho /= (4.0*math.pi) def calculate_hartree(self, rho): """Calculate the Hartree potential from a given density""" npoints = self.grid.npoints r, r2, dr, dx = self.grid.r, self.grid.r2, self.grid.dr, self.grid.dx # running charge q = numpy.zeros(npoints) for i in xrange(1,npoints): q[i] = q[i-1] + 4.0*math.pi*r2[i] * dx*dr[i] * rho[i] # hartree v_h = numpy.zeros(npoints) v_h[-1] = q[-1]/r[-1] for i in xrange(npoints-1,0,-1): v_h[i-1] = v_h[i] + dx * q[i]*self.grid.dr[i]/self.grid.r2[i] e_h = 0.5*self.grid.integrate_two(v_h, rho * r2) * 4.0*math.pi return e_h, v_h def calculate_xc(self, rho): """Calculate the XC potential from a given density""" npoints = self.grid.npoints r, r2, dr, dx = self.grid.r, self.grid.r2, self.grid.dr, self.grid.dx e_x = numpy.zeros(npoints) v_x = numpy.zeros(npoints) e_c = numpy.zeros(npoints) v_c = numpy.zeros(npoints) for i in xrange(len(rho)): (e_x[i], v_x[i]), (e_c[i], v_c[i]) = self.xc.xc(rho[i]) v_xc = v_x + v_c e_xc = self.grid.integrate_two(e_x + e_c, rho * r2) * 4.0*math.pi e_vxc = self.grid.integrate_two(v_xc, rho * r2) * 4.0*math.pi return e_xc, v_xc, e_vxc def calculate_v_of_rho(self): """Calculate Hartree and XC, potential and energy""" self.e_h, self.v_h = self.calculate_hartree(self.rho) self.e_ion = self.grid.integrate_two(self.v_ion, self.rho*self.grid.r2) * 4.0*math.pi self.e_xc, self.v_xc, self.e_vxc = self.calculate_xc(self.rho) def __getstate__(self): """In order to implement pickle""" odict = self.__dict__.copy() del odict['out'] return odict