def test_flosic_scf(self): # FLO-SIC SCF xc = 'LDA,PW' # Exchange-correlation functional in the form: (exchange,correlation) grid_level = 3 # Level of the numerical grid. 3 is the standard value vsic_every = 1 # Calculate VSIC after 3 iterations mf = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2, grid_level=grid_level, vsic_every=vsic_every) mf.max_cycle = 300 # Number of SCF iterations. mf.conv_tol = 1e-6 # Accuracy of the SCF cycle. mf.verbose = 4 # Amount of output. 4: full output. e_ref = -40.69756811131563 self.assertAlmostEqual(mf.kernel(), e_ref, 5)
mol.max_memory = 2000 mol.build() xc = 'LDA,PW' # quick dft calculation mdft = dft.UKS(mol) mdft.xc = xc mdft.kernel() # build O(N) stuff myon = ON(mol,[fod1.positions,fod2.positions], grid_level=grid_level) myon.nshell = 1 myon.build() # enable ONMSH m = FLOSIC(mol,xc=xc,fod1=fod1,fod2=fod2,grid_level=grid_level, init_dm=mdft.make_rdm1(),ham_sic='HOOOV') m.max_cycle = 40 m.set_on(myon) m.conv_tol = 1e-5 # In-SCF-FOD optimization m.preopt = True m.preopt_start_cycle=0 m.preopt_fix1s = True m.preopt_fmin = 0.005 m.kernel() # Some output print(m.fod_gradients()) print(m.get_fforces())
atoms = read('HHeH_s2.xyz') # Now, we set up the mole object. spin = 2 charge = 0 xc = 'LDA,PW' b = 'cc-pVQZ' [geo,nuclei,fod1,fod2,included] = xyz_to_nuclei_fod(atoms) mol = gto.M(atom=ase2pyscf(nuclei), basis=b,spin=spin,charge=charge) #mol.verbose = 4 # Now we set up the calculator. sic_object = FLOSIC(mol=mol,xc=xc,fod1=fod1,fod2=fod2,ham_sic='HOO') sic_object.conv_tol = 1e-7 sic_object.max_cycle = 300 # With this, we can calculate the high-spin total ground state energy. E_HS_SIC = sic_object.kernel() # Next, we need the low-spin solution. The first steps are completely similar to the high-spin calculation. atoms = read('HHeH_s0.xyz') spin = 0 charge = 0 xc = 'LDA,PW' b = 'cc-pVQZ' [geo,nuclei,fod1,fod2,included] = xyz_to_nuclei_fod(atoms)
# For this we first need a mole object. # The test system will be CH4. sysname = 'CH4' molecule = read(sysname + '.xyz') geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) spin = get_multiplicity(sysname) # We will use a smaller basis set to keep computational cost low. # The better the basis set, the nicer the FLO will look. b = 'sto3g' mol = gto.M(atom=ase2pyscf(nuclei), basis={'default': b}, spin=spin) # Now we can initiliaze the SIC object. sic_object = FLOSIC(mol, fod1=fod1, fod2=fod2, xc='LDA,PW') # And perform a ground state calculation. sic_object.max_cycle = 500 sic_object.kernel() # Now we can call the visualization function. The sysname here only specifies the output name. # Maxcell coordinates the size of the cell that can be seen in VESTA. print_flo(sic_object, sic_object.flo, sysname, nuclei, fod1, fod2, maxcell=6.) # The .cube files will appear in this folder. An example .png of how they should look is provided as CH4.png
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) mf2.max_cycle = maxcycle mf2.conv_tol = convtol mf2.grids.level = 4 e = mf2.kernel() # Get the FLOSIC density. flo = mf2.flo ao = dft.numint.eval_ao(mol, mf.grids.coords, deriv=0) dm_flo = dynamic_rdm(flo, mf2.mo_occ) rho_flo_scf = dft.numint.eval_rho(mol, ao, dm_flo[0], None, 'LDA', 0, None) # Plot the densities. # Init the arrays.
spin=spin, charge=charge) # We further need to specify the numerical grid level used in the self-consistent FLO-SIC calculation. grid_level = 4 # We need to choose an exchange-correlation functional. xc = 'LDA,PW' # Exchange-correlation functional in the form: (exchange,correlation) # NOTE: As there exists only one way to express the exchange for LDA, there is only one identifier. # For LDA correlation there exist several. # Now we can initiliaze the SIC object. sic_object = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2, grid_level=grid_level) # We can the modify the SIC object now if wished. sic_object.max_cycle = 300 # Number of SCF iterations. sic_object.conv_tol = 1e-7 # Accuracy of the SCF cycle. sic_object.verbose = 4 # Amount of output. 4: full output. # Now we can start the SIC calculation. The main output, the total energy, is the direct return value. total_energy_sic = sic_object.kernel() # We can get further output by accessing the attributes of the SIC object. homo_flosic = sic_object.homo_flosic
# Drawing on these, the PySCF intrinsic functions .newton() and .scanner() allow the usage of a Newton optimization algorithm. # Such an algorithm constructs the Hessian from these derivatives and uses it to perform anoptimization that is faster than the regular SCF solver. # Unfortunately, it can ONLY BE USED WITH LDA OR GGA. # PySCF currently DOES NOT SUPPORT MGGAs with this second order solver. # To show the speedup, the calculations below will be timed. # To use this solver, we first have to simply define a regular calculator. # The testing system is a Li atom. molecule = read('Li.xyz') geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) spin = 1 charge = 0 b = 'sto3g' xc = 'LDA,PW' mol = gto.M(atom=ase2pyscf(nuclei), basis=b, spin=spin, charge=charge) sic_object = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2) sic_object.max_cycle = 400 # Perform a calculation with the regular sovler. start_regular = time.time() sic_object.kernel() end_regular = time.time() duration_regular = end_regular - start_regular # Now enable the second order solver. sic_object = sic_object.as_scanner() sic_object = sic_object.newton() # And perform a calculation with the second order solver.
spin=spin_2, charge=charge) # We further need to specify the numerical grid level used in the self-consistent FLO-SIC calculation. grid_level = 4 # We need to choose an exchange-correlation functional. xc = 'LDA,PW' # Exchange-correlation functional in the form: (exchange,correlation) # NOTE: As there exists only one way to express the exchange for LDA, there is only one identifier. # For LDA correlation there exist several. # Now we can initiliaze the SIC objects. sic_0 = FLOSIC(mol_0, xc=xc, fod1=fod1, fod2=fod2, grid_level=grid_level) # For spin = 2 we have to adjust the FOD geometry by moving the FOD for the second spin channel into the first one. fod1_spin2 = fod1.copy() fod2_spin2 = fod2.copy() fod1_spin2.append(fod2_spin2[0]) del fod2_spin2[0] sic_2 = FLOSIC(mol_2, xc=xc, fod1=fod1_spin2, fod2=fod2_spin2, grid_level=grid_level) # To enable a variable spin configuration, we simply have to load the routine sic_occ_
from ase.io import read except ImportError: raise SystemExit('The package \'ase\' is required for this example!') try: from flosic_os import ase2pyscf, xyz_to_nuclei_fod from flosic_scf import FLOSIC except ImportError: raise SystemExit('The package \'pyflosic\' is required for this example!') from pyscf import gto from var_mesh import var_mesh # Set up calculation details molecule = read('H2.xyz') geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) mol = gto.M(atom=ase2pyscf(nuclei), basis='6-311++Gss', spin=0, charge=0) sic_object = FLOSIC(mol, xc='lda,pw', fod1=fod1, fod2=fod2, ham_sic='HOO') sic_object.max_cycle = 300 sic_object.conv_tol = 1e-7 # By default a grid level of 3 is used, save its size mesh_size = len(sic_object.calc_uks.grids.coords) sic_object.calc_uks.grids = var_mesh(sic_object.calc_uks.grids) # Start the calculation total_energy_sic = sic_object.kernel() homo_flosic = sic_object.homo_flosic # Display mesh sizes and energy values print('Mesh size before: %d' % mesh_size) print('Mesh size after: %d' % len(sic_object.calc_uks.grids.coords)) print('Total energy of H2 (FLO-SIC SCF): %0.5f (should be %0.5f)' %
# Unfortunately, it can ONLY BE USED WITH LDA OR GGA. # PySCF currently DOES NOT SUPPORT MGGAs with this second order solver. # To show the speedup, the calculations below will be timed. # 1) Standard example WITHOUT usage of fixed VSIC properties # To use this solver, we first have to simply define a regular calculator. # The testing system is a Li atom. molecule = read('Li.xyz') geo,nuclei,fod1,fod2,included = xyz_to_nuclei_fod(molecule) spin = 1 charge = 0 b = 'sto3g' xc = 'LDA,PW' mol = gto.M(atom=ase2pyscf(nuclei), basis=b,spin=spin,charge=charge) mf = FLOSIC(mol,xc=xc,fod1=fod1,fod2=fod2) # Perform a calculation with the regular sovler. t1_start = time.time() e1 = mf.kernel() t1_end = time.time() delta_t1 = t1_end-t1_start # 2) Standard example WITH usage of fixed VSIC properties # To use this solver, we first have to simply define a regular calculator. # The testing system is a Li atom. molecule = read('Li.xyz') geo,nuclei,fod1,fod2,included = xyz_to_nuclei_fod(molecule)
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()
grid_level = 5 mol.verbose = 4 mol.max_memory = 2000 mol.build() xc = 'LDA,PW' # quick dft calculation mdft = dft.UKS(mol) mdft.xc = xc mdft.kernel() # build FLOSIC object mflosic = FLOSIC(mol, xc=xc, fod1=fodup, fod2=foddn, grid_level=grid_level, init_dm=mdft.make_rdm1()) mflosic.max_cycle = 40 mflosic.conv_tol = 1e-5 # just for testing calc = BasicFLOSICC(atoms=fod, mf=mflosic) #print(fod.get_potential_energy()) #print(fod.get_forces()) print(" >>>>>>>>>>>> OPTIMIZER TEST <<<<<<<<<<<<<<<<<<<<") # test the calculator by optimizing a bit from ase.optimize import FIRE dyn = FIRE( atoms=fod,
xc = 'LDA,PW' x = 'LDA,' # Now we can build the mole object. mol = gto.M(atom=ase2pyscf(nuclei), basis=b, spin=spin, charge=charge) # The next part is the definition of the calculator objects. # For both xc and x we create a separate calculator object. dftx = dft.UKS(mol) dftx.xc = x dftxc = dft.UKS(mol) dftxc.xc = xc sicx = FLOSIC(mol, xc=x, fod1=fod1, fod2=fod2) sicxc = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2) # Now we need to do the ground state calculations. etot_dftx = dftx.kernel() etot_dftxc = dftxc.kernel() etot_sicx = sicx.kernel() etot_sicxc = sicxc.kernel() # Then we can access the xc energies. veffdftx = dftx.get_veff(mol=mol) exc_dftx = veffdftx.__dict__['exc'] veffdftxc = dftxc.get_veff(mol=mol) exc_dftxc = veffdftxc.__dict__['exc']
# The testing system will be an H4 structure (two H2 molecules.) # First, we need the calculator objects and therefore mole objects. molecule = read('H4.xyz') geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) spin = 0 charge = 0 b = 'cc-pVQZ' xc = 'LDA,PW' mol = gto.M(atom=ase2pyscf(nuclei), basis=b, spin=spin, charge=charge) # Now we can initiliaze the calculator objects. dft_object = dft.UKS(mol) dft_object.xc = xc sic_object = FLOSIC(mol, xc=xc, fod1=fod1, fod2=fod2, ham_sic='HOO') # To calculate alpha we first calculate mu WITHOUT an electric field. dft_object.kernel() dft_mu0 = dft_object.dip_moment()[-1] sic_object.kernel() sic_mu0 = sic_object.dip_moment()[-1] # Now we apply the electric field to the DFT and FLO-SIC calculator objects. # For this we first set the gauge origin for the dipole integral. mol.set_common_orig([0., 0., 0.]) # Then we calculate the new external potential.