def test_flosic_os(self): # DFT mf = dft.UKS(mol) mf.verbose = 4 # Amount of output. 4: full output. mf.max_cycle = 300 # Number of SCF iterations. mf.conv_tol = 1e-6 # Accuracy of the SCF cycle. mf.grids.level = 7 # Level of the numerical grid. 3 is the standard value. mf.xc = 'LDA,PW' # Exchange-correlation functional in the form: (exchange,correlation) mf.kernel() # FLO-SIC OS results = flosic(mol, mf, fod1, fod2) e_calc = results['etot_sic'] e_ref = -40.69057092300857 self.assertAlmostEqual(e_calc, e_ref, 5)
charge=charge) # Set up the DFT calculation. dft_object = dft.UKS(mol) dft_object.verbose = verbose dft_object.max_cycle = max_cycle dft_object.conv_tol = conv_tol dft_object.grids.level = grids_level dft_object.xc = xc # Perform the DFT calculation. dft_energy = dft_object.kernel() # Apply FLO-SIC to the DFT calculation. flosic_values_2 = flosic(mol, dft_object, fod1, fod2) # Output the results. The output for FLO-SIC is given in the form of Python dictionaries. print("ESIC: {}".format(flosic_values_1['etot_sic'] - dft_energy)) print('Total energy of H2 (DFT): %0.5f (should be %0.5f)' % (dft_energy, -1.13634167738585)) print('Total energy of H2 (FLO-SIC FULL): %0.5f (should be %0.5f) ' % (flosic_values_1['etot_sic'], -1.18032726019)) print( 'Total energy of H2 (FLO-SIC POST-PROCESSING): % 0.5f (should be %0.5f) ' % (flosic_values_2['etot_sic'], -1.18032726019))
mf.max_cycle = maxcycle mf.conv_tol = convtol mf.grids.level = gridlevel mf.xc = xc mf.kernel() # Get the DFT density. ao = dft.numint.eval_ao(mol, mf.grids.coords, deriv=0) dm_dft = mf.make_rdm1() rho_dft = dft.numint.eval_rho(mol, ao, dm_dft[0], None, 'LDA', 0, None) # Do the FLOSIC OS. print('Starting FLOSIC calculation in OS mode.') flosic_values = flosic(mol, mf, fod1, fod2) flo = flosic_values['flo'] # Get the FLOSIC OS density. dm_flo = dynamic_rdm(flo, mf.mo_occ) rho_flo_os = dft.numint.eval_rho(mol, ao, dm_flo[0], None, 'LDA', 0, None) # Get the mesh. mesh = mf.grids.coords # Do the FLOSIC SCF. print('Starting FLOSIC calculation in SCF mode.') mf2 = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2)
def get_flosic_veff(ks, mol=None, dm=None, dm_last=0, vhf_last=0, hermi=1): # pyscf standard call for scf cycle 0. veff = uks.get_veff(ks=ks.calc_uks, mol=ks.mol, dm=dm, dm_last=dm_last, vhf_last=vhf_last, hermi=hermi) if mol is None: mol = ks.mol # Build the hamiltonian to get the KS wave functions. dim = np.shape(ks.calc_uks.mo_coeff) s1e = ks.get_ovlp(mol) h1e = ks.get_hcore(mol) hamil = ks.get_fock(h1e, s1e, vhf_last, dm) # Get the KSO. ks_new = np.zeros((2, dim[1], dim[1]), dtype=np.float64) try: if dm_last == 0: # First SCF cycle: do nothing. pass except: # Every other DFT cycle: Build the KS wavefunctions with the Hamiltonian, then give them to the UKS object that is the input for flosic. trash, ks_new = ks.eig(hamil, s1e) ks_inter = np.array(ks_new) # Update UKS object. ks.calc_uks.mo_coeff = ks_inter.copy() # If ldax is enabled, the xc functional is set to LDA exchange only. if ks.ldax == True: xc_sav = ks.calc_uks.xc ks.calc_uks.xc = 'LDA,' # Call the FLOSIC routine with the UKS object. # This for the fixed Vsic modus. # If Vsic values are present and the Vsic potential should not # be updated use these values. # (THa: the outer if ... clause was added to prevent the # sic potentials to be calculated during initialization) _t0 = time.time() #print('>> ks.fixed_vsic', ks.fixed_vsic) #print('>>', ks.neval_vsic) #sys.exit() if ks.neval_vsic > -1: if ks.fixed_vsic != 0.0 and (ks.num_iter % ks.vsic_every) != 0: if ks.verbose >= 4: print('Use fixed Vsic (cycle = %i)' % ks.num_iter) flo_veff = flosic(ks.mol, ks.calc_uks, ks.fod1, ks.fod2,\ datatype=np.float64,calc_forces=ks.calc_forces,debug=ks.debug,\ nuclei=ks.nuclei,l_ij=ks.l_ij,ods=ks.ods,\ fixed_vsic=ks.fixed_vsic,ham_sic=ks.ham_sic) # If no Vsic values are present or the the Vsic values should be # updated calcualte new Vsic values. # !! THa: Possible BUG # ks.fixed_vsic == 0.0 may never be 'True' because # a float-value is amost never exactly zero # better use: np.isclose(ks.fixed_vsic, 0.0) # !! if ks.fixed_vsic == 0.0 or (ks.num_iter % ks.vsic_every) == 0: if ks.verbose >= 4: print('Calculate new Vsic (cycle = %i)' % ks.num_iter) flo_veff = flosic(ks.mol,ks.calc_uks,ks.fod1,ks.fod2,\ datatype=np.float64,calc_forces=ks.calc_forces,debug=ks.debug,\ nuclei=ks.nuclei,l_ij=ks.l_ij,ods=ks.ods,ham_sic=ks.ham_sic) ks.fixed_vsic = flo_veff['fixed_vsic'] else: flo_veff = veff _t1 = time.time() if mol.verbose > 3: print("FLO-SIC time for SIC potential: {0:0.1f} [s]".format(_t1 - _t0)) # increase a magic counter ks.num_iter = ks.num_iter + 1 # If ldax is enabled, the change to xc is only meant for the FLO-SIC part and # therefore has to be changed back. if ks.ldax == True: ks.calc_uks.xc = xc_sav # Assign the return values. # The total energies of DFT and FLO-SIC if ks.neval_vsic > -1: sic_etot = flo_veff['etot_sic'] dft_etot = flo_veff['etot_dft'] # The FLOs. ks.flo = flo_veff['flo'] # The FOD forces. ks.fforces = flo_veff['fforces'] # The FLO-SIC H**O energy eigenvalue. ks.homo_flosic = flo_veff['homo_sic'] ks.evalues = flo_veff['evalues'] ks.lambda_ij = flo_veff['lambda_ij'] # Developer modus: atomic forces (AF) if ks.debug == True: ks.AF = flo_veff['AF'] else: sic_etot = ks.e_tot dft_etot = ks.e_tot ks.flo = ks.mo_coeff ks.homo_flosic = 0.0 try: # First SCF cycle: veff = veff_dft and the SIC is zero. if dm_last == 0: sic_veff = veff sic_etot = dft_etot except: # Every other DFT cycle: Build veff as sum of the regular veff and the SIC # potential. sic_veff = veff + flo_veff['hamil'] # Update the density matrix. dm_new = dynamic_rdm(ks.flo, ks.calc_uks.mo_occ) dm = dm_new.copy() ks.mo_coeff = ks.flo # Give back the FLO-SIC energy correction and the corrected potential. This libtagarray # formalism is defined by pyscf. sic_back = sic_etot - dft_etot veff_sic = lib.tag_array(sic_veff, ecoul=veff.ecoul, exc=veff.exc, vj=veff.vj, vk=veff.vk, esic=(sic_back)) # Return the exchange-correlation energy and the FLO-SIC energy correction. ks.exc = veff.exc ks.esic = sic_back # increase another magic counter ;-) ks.neval_vsic += 1 return veff_sic
def calculate(self, atoms, properties=['energy'], system_changes=['positions']): self.num_iter += 1 atoms = self.get_atoms() self.atoms = atoms Calculator.calculate(self, atoms, properties, system_changes) if self.mode == 'dft': # DFT only mode from pyscf import gto, scf, grad, dft [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms) nuclei = ase2pyscf(nuclei) mol = gto.M(atom=nuclei, basis=self.basis, spin=self.spin, charge=self.charge) mf = scf.UKS(mol) mf.xc = self.xc # Verbosity of the mol object (o lowest output, 4 might enough output for debugging) mf.verbose = self.verbose mf.max_cycle = self.max_cycle mf.conv_tol = self.conv_tol mf.grids.level = self.grid if self.n_rad is not None and self.n_ang is not None: mf.grids.atom_grid = (self.n_rad, self.n_ang) mf.grids.prune = prune_dict[self.prune] e = mf.kernel() self.mf = mf self.results['energy'] = e * Ha self.results['dipole'] = dipole = mf.dip_moment(verbose=0) self.results['evalues'] = mf.mo_energy if self.mode == 'flosic-os': # FLOSIC SCF mode from pyscf import gto, scf [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms) # FLOSIC one shot mode #mf = flosic(self.atoms,charge=self.charge,spin=self.spin,xc=self.xc,basis=self.basis,debug=False,verbose=self.verbose) # Effective core potentials need so special treatment. if self.ecp == None: if self.ghost == False: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge) if self.ghost == True: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge) mol.basis = { 'default': self.basis, 'GHOST1': gto.basis.load('sto3g', 'H'), 'GHOST2': gto.basis.load('sto3g', 'H') } if self.ecp != None: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge, ecp=self.ecp) mf = scf.UKS(mol) mf.xc = self.xc # Verbosity of the mol object (o lowest output, 4 might enough output for debugging) mf.verbose = self.verbose # Binary output format of pyscf. # Save MOs, orbital energies, etc. if self.use_chk == True and self.use_newton == False: mf.chkfile = 'pyflosic.chk' # Load from previous run, if exist, the checkfile. # Hopefully this will speed up the calculation. if self.use_chk == True and self.use_newton == False and os.path.isfile( 'pyflosic.chk'): mf.init_guess = 'chk' mf.update('pyflosic.chk') if self.use_newton == True: mf = mf.as_scanner() mf = mf.newton() mf.max_cycle = self.max_cycle mf.conv_tol = self.conv_tol mf.grids.level = self.grid if self.n_rad is not None and self.n_ang is not None: mf.grids.atom_grid = (self.n_rad, self.n_ang) mf.grids.prune = prune_dict[self.prune] e = mf.kernel() self.mf = mf mf = flosic(mol, mf, fod1, fod2, sysname=None, datatype=np.float64, print_dm_one=False, print_dm_all=False, debug=self.debug, calc_forces=True, ham_sic=self.ham_sic) self.results['energy'] = mf['etot_sic'] * Ha # unit conversion from Ha/Bohr to eV/Ang #self.results['fodforces'] = -1*mf['fforces']/(Ha/Bohr) self.results['fodforces'] = -1 * mf['fforces'] * (Ha / Bohr) print('Analytical FOD force [Ha/Bohr]') print(mf['fforces']) print('fmax = %0.6f [Ha/Bohr]' % np.sqrt( (mf['fforces']**2).sum(axis=1).max())) self.results['dipole'] = mf['dipole'] self.results['evalues'] = mf['evalues'] if self.mode == 'flosic-scf': #if self.mf is None: # FLOSIC SCF mode from pyscf import gto [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms) # Effective core potentials need so special treatment. if self.ecp == None: if self.ghost == False: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge) if self.ghost == True: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge) mol.basis = { 'default': self.basis, 'GHOST1': gto.basis.load('sto3g', 'H'), 'GHOST2': gto.basis.load('sto3g', 'H') } if self.ecp != None: mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis, spin=self.spin, charge=self.charge, ecp=self.ecp) if self.efield != None: m0 = FLOSIC(mol=mol, xc=self.xc, fod1=fod1, fod2=fod2, grid_level=self.grid, debug=self.debug, l_ij=self.l_ij, ods=self.ods, fixed_vsic=self.fixed_vsic, num_iter=self.num_iter, vsic_every=self.vsic_every, ham_sic=self.ham_sic) # test efield to enforce some pseudo chemical environment # and break symmetry of density m0.grids.level = self.grid m0.conv_tol = self.conv_tol # small efield m0.max_cycle = 1 h = -0.0001 #-0.1 apply_field(mol, m0, E=(0, 0, 0 + h)) m0.kernel() mf = FLOSIC(mol=mol, xc=self.xc, fod1=fod1, fod2=fod2, grid_level=self.grid, calc_forces=self.calc_forces, debug=self.debug, l_ij=self.l_ij, ods=self.ods, fixed_vsic=self.fixed_vsic, num_iter=self.num_iter, vsic_every=self.vsic_every, ham_sic=self.ham_sic) # Verbosity of the mol object (o lowest output, 4 might enough output for debugging) mf.verbose = self.verbose # Binary output format of pyscf. # Save MOs, orbital energies, etc. if self.use_chk == True and self.use_newton == False: mf.chkfile = 'pyflosic.chk' # Load from previous run, if exist, the checkfile. # Hopefully this will speed up the calculation. if self.use_chk == True and self.use_newton == False and os.path.isfile( 'pyflosic.chk'): mf.init_guess = 'chk' mf.update('pyflosic.chk') if self.use_newton == True: mf = mf.as_scanner() mf = mf.newton() mf.max_cycle = self.max_cycle mf.conv_tol = self.conv_tol mf.grids.level = self.grid if self.n_rad is not None and self.n_ang is not None: mf.grids.atom_grid = (self.n_rad, self.n_ang) mf.calc_uks.grids.atom_grid = (self.n_rad, self.n_ang) mf.grids.prune = prune_dict[self.prune] mf.calc_uks.grids.prune = prune_dict[self.prune] e = mf.kernel() self.mf = mf # Return some results to the pyflosic_ase_caculator object. self.results['esic'] = mf.esic * Ha self.results['energy'] = e * Ha self.results['fixed_vsic'] = mf.fixed_vsic # if self.mf is not None: # from pyscf import gto # [geo,nuclei,fod1,fod2,included] = xyz_to_nuclei_fod(self.atoms) # # Effective core potentials need so special treatment. # if self.ecp == None: # if self.ghost == False: # mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge) # if self.ghost == True: # mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge) # mol.basis ={'default':self.basis,'GHOST1':gto.basis.load('sto3g', 'H'),'GHOST2':gto.basis.load('sto3g', 'H')} # if self.ecp != None: # mol = gto.M(atom=ase2pyscf(nuclei), basis=self.basis,spin=self.spin,charge=self.charge,ecp=self.ecp) # self.mf.num_iter = self.num_iter # self.mf.max_cycle = self.max_cycle # self.mf.mol = mol # self.mf.fod1 = fod1 # self.mf.fod2 = fod2 # e = self.mf.kernel() # # Return some results to the pyflosic_ase_caculator object. # self.results['esic'] = self.mf.esic*Ha # self.results['energy'] = e*Ha # self.results['fixed_vsic'] = self.mf.fixed_vsic # if self.fopt == 'force' or self.fopt == 'esic-force': # # The standard optimization uses # the analytical FOD forces # fforces = self.mf.get_fforces() #fforces = -1*fforce # unit conversion Hartree/Bohr to eV/Angstroem #self.results['fodforces'] = -1*fforces*(Ha/Bohr) self.results['fodforces'] = fforces * (Ha / Bohr) print('Analytical FOD force [Ha/Bohr]') print(fforces) print('fmax = %0.6f [Ha/Bohr]' % np.sqrt( (fforces**2).sum(axis=1).max())) if self.fopt == 'lij': # # This is under development. # Trying to replace the FOD forces. # self.lambda_ij = self.mf.lambda_ij self.results['lambda_ij'] = self.mf.lambda_ij #fforces = [] #nspin = 2 #for s in range(nspin): # # printing the lampda_ij matrix for both spin channels # print 'lambda_ij' # print lambda_ij[s,:,:] # print 'RMS lambda_ij' # M = lambda_ij[s,:,:] # fforces_tmp = (M-M.T)[np.triu_indices((M-M.T).shape[0])] # fforces.append(fforces_tmp.tolist()) #print np.array(fforces).shape try: # # Try to calculate the FOD forces from the differences # of SIC eigenvalues # evalues_old = self.results['evalues'] print(evalues_old) evalues_new = self.mf.evalues print(evalues_new) delta_evalues_up = (evalues_old[0][0:len(fod1)] - evalues_new[0][0:len(fod1)]).tolist() delta_evalues_dn = (evalues_old[1][0:len(fod2)] - evalues_new[1][0:len(fod2)]).tolist() print(delta_evalues_up) print(delta_evalues_dn) lij_force = delta_evalues_up lij_force.append(delta_evalues_dn) lij_force = np.array(lij_force) lij_force = np.array(lij_force, (np.shape(lij_force)[0], 3)) print('FOD force evalued from evalues') print(lij_force) self.results['fodforces'] = lij_force except: # # If we are in the first iteration # we can still use the analystical FOD forces # as starting values # fforces = self.mf.get_fforces() print(fforces) #self.results['fodforces'] = -1*fforces*(Ha/Bohr) self.results['fodforces'] = fforces * (Ha / Bohr) print('Analytical FOD force [Ha/Bohr]') print(fforces) print('fmax = %0.6f [Ha/Bohr]' % np.sqrt( (fforces**2).sum(axis=1).max())) self.results['dipole'] = self.mf.dip_moment() self.results['evalues'] = self.mf.evalues if atoms is not None: self.atoms = atoms.copy()