def automatic_guessing(ase_nuclei, charge, spin, basis, xc, method='FB', ecp=None, newton=False, grid=3, BS=None, calc='UKS', symmetry=False, verbose=4): # ase_nuclei_atoms ... ase.atoms.object containg only nuclei positions # charge ... charge of the system # spin ... spin state of the system # basis ... basis set # xc ... exchange-correlation functional # method ... localization method (FB, ER, PM etc.) # Note: FB seems to give very reasonable guesses. # ecp ... effective core potential file # newton ... second order Newton Raphston scf solver works for LDA and GGA not for SCAN # grid ... grid level # BS ... broken symmetry # calc ,,, UKS or UHF # Performe a DFT calculation. method = method.upper() calc = calc.upper() ase_atoms = ase_nuclei if ecp is None: mol = gto.M(atom=ase2pyscf(ase_atoms), basis=basis, spin=spin, charge=charge, symmetry=symmetry) if ecp is not None: mol = gto.M(atom=ase2pyscf(ase_atoms), basis=basis, ecp=ecp, spin=spin, charge=charge, symmetry=symmetry) mol.verbose = verbose if calc == 'UKS': mf = scf.UKS(mol) if calc == 'UHF': mf = scf.UHF(mol) if calc == 'RHF': mf = scf.RHF(mol) mf.grids.level = grid mf.max_cycle = 3000 mf.xc = xc # Broken symmetry if BS != None: mf.kernel() idx_flip = mol.search_ao_label(BS) dma, dmb = mf.make_rdm1() dma_flip = dma[idx_flip.reshape(-1, 1), idx_flip].copy() dmb_flip = dmb[idx_flip.reshape(-1, 1), idx_flip].copy() dma[idx_flip.reshape(-1, 1), idx_flip] = dmb_flip dmb[idx_flip.reshape(-1, 1), idx_flip] = dma_flip dm = [dma, dmb] if ecp is None: mol = gto.M(atom=ase2pyscf(ase_atoms), basis=basis, spin=0, charge=charge, symmetry=symmetry) if ecp is not None: mol = gto.M(atom=ase2pyscf(ase_atoms), basis=basis, ecp=ecp, spin=0, charge=charge, symmetry=symmetry) mol.verbose = verbose mf = scf.UKS(mol) mf.grids.level = grid mf.max_cycle = 3000 mf.xc = xc if newton == True: mf = mf.as_scanner() mf = mf.newton() if BS == None: mf.kernel() if BS != None: mf.run(dm) if calc == 'RHF': mf = scf.addons.convert_to_uhf(mf) # Performe a localization calculation. # Both spin channels are localized separately. # The orbitals are written out as cube files. calc_localized_orbitals(mf, mol, method=method) # Collect all cube files per spin channel. f1 = glob.glob('*orb*spin1.cube') f2 = glob.glob('*orb*spin2.cube') # test for nuclei positions # for ASE Version: 3.15.1b1 # we neede the file handle and not the string # new: f_cube = open(f1[0]) ase_atoms = cube.read_cube(f_cube) # previous: ase_atoms = cube.read_cube(f1[0]) f_cube.close() # Calculate the guess. get_guess(atoms=ase_atoms, spin1_cube=f1, spin2_cube=f2, method=method)
from ase.io import read from flosic_os import xyz_to_nuclei_fod, ase2pyscf, flosic from flosic_scf import FLOSIC from nrlmol_basis import get_dfo_basis from ase_pyflosic_optimizer import flosic_optimize from ase.units import Ha # Geometry #f_xyz = '../examples/advanced_applications/CH4.xyz' f_xyz = 'CH4.xyz' sysname = 'CH4' molecule = read(f_xyz) geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) spin = 0 charge = 0 mol = gto.M(atom=ase2pyscf(nuclei), basis=get_dfo_basis(sysname), spin=spin, charge=charge) class KnownValues(unittest.TestCase): def test_dft(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) e_ref = -40.1187154486949
# Installation of pyberny # pip3 install pyberny # Read the structure. # This starting structure includes both nuclei and FOD positions. ase_atoms = read('LiH.xyz') # We want only to optimize the nuclear positions. [geo,nuclei,fod1,fod2,included] = xyz_to_nuclei_fod(ase_atoms) ase_atoms = nuclei # Calulation parameters. charge = 0 spin = 0 basis = get_dfo_basis('LiH') xc = 'LDA,PW' # Set up the pyscf structure object. mol = gto.M(atom=ase2pyscf(ase_atoms),spin=spin,charge=charge,basis=basis) mol.verbose = 4 # DFT pyscf calculation. mf = dft.UKS(mol) mf.max_cycle = 300 mf.conv_tol = 1e-6 mf.xc = xc mf = mf.newton() mf = mf.as_scanner() # SCF single point for starting geometry. e = mf.kernel() # Starting Gradients gf = uks.Gradients(mf) gf.grid_response = True gf.kernel() # Optimization
# "Calculation of magnetic coupling constants with hybrid density functionals" # @ https://jyx.jyu.fi/dspace/bitstream/handle/123456789/45989/URN%3ANBN%3Afi%3Ajyu-201505211947.pdf?sequence=1, Eq.(84) # for further information. return -2*(HS - LS)/(S2_HS-S2_LS)*219474 # First, we read the structure for the high spin configuration. atoms = read('HHeH.xyz') # Now, we set up the mole object. spin = 2 charge = 0 xc = 'LDA,PW' b = 'cc-pVQZ' mol = gto.M(atom=ase2pyscf(atoms), basis=b,spin=spin,charge=charge) # Now we set up the calculator. dft_object = dft.UKS(mol) dft_object.max_cycle= 300 dft_object.xc = xc dft_object.conv_tol = 1e-7 # With this, we can calculate the high-spin total ground state energy. E_HS_DFT = dft_object.kernel() # Next, we need the low-spin solution. The first steps are completely similar to the high-spin calculation. # In addition we now apply a small electric field.
import matplotlib.pyplot as plt # This example shows how the density can be visualized on the numerical grid. # The routines provided by plot_density.py are very straightforward and only need a system name in order to perform this visualization. # The default plotting axis is the z-axis; modify the routine in whatever way you wish. # The only input we need are a mole object, the system name (only for output purposes) and the FOD geometry. # The example system will be an H2 molecule with spin 2. # We first have to set up a mole object. sysname = 'H2' molecule = read('H2_stretched_density.xyz') geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) spin = 2 b = 'cc-pvqz' mol = gto.M(atom=ase2pyscf(nuclei), basis={'default': b}, spin=spin) # Set the calculation parameters. gridlevel = 4 convtol = 1e-6 maxcycle = 50 xc = 'LDA,PW' # Do the DFT calculation. print('Starting DFT calculation.') mf = dft.UKS(mol) mf.max_cycle = maxcycle mf.conv_tol = convtol mf.grids.level = gridlevel
try: 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))
geo, nuclei, fod1, fod2, included = xyz_to_nuclei_fod(molecule) # We also need the spin and the charge. spin_0 = 0 spin_2 = 2 charge = 0 # Furthermore we have to pick a basis set. # We use the minimal basis set here in order to keep computational cost low. b = 'sto3g' # With that we can build the mole object. mol_0 = gto.M(atom=ase2pyscf(nuclei), basis={'default': b}, spin=spin_0, charge=charge) mol_2 = gto.M(atom=ase2pyscf(nuclei), basis={'default': b}, 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)
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()
def get_forces(self, atoms=None): if atoms != None: self.atoms = atoms # get nuclei and FOD forces # calculates forces if required if self.atoms == None: self.atoms = atoms # Note: The gradients for UKS are only available in the dev branch of pyscf. if self.mode == 'dft' or self.mode == 'both': from pyscf.grad import uks if self.mf == None: from pyscf import gto, scf, 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 mf.conv_tol = self.conv_tol mf.max_cycle = self.max_cycle mf.verbose = self.verbose 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] if self.xc == 'LDA,PW' or self.xc == 'PBE,PBE': # The 2nd order scf cycle (Newton) speed up calculations, # but does not work for MGGAs like SCAN,SCAN. mf = mf.as_scanner() mf = mf.newton() mf.kernel() self.mf = mf gf = uks.Gradients(mf) forces = gf.kernel() #if self.mf != None: # gf = uks.Gradients(self.mf) # forces = gf.kernel() gf = uks.Gradients(self.mf) forces = gf.kernel() * (Ha / Bohr) #print(forces) if self.mode == 'flosic-os' or self.mode == 'flosic-scf': [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms) forces = np.zeros_like(nuclei.get_positions()) if self.mode == 'dft': # mode for nuclei only optimization (fods fixed) forces = forces.tolist() totalforces = [] totalforces.extend(forces) [geo, nuclei, fod1, fod2, included] = xyz_to_nuclei_fod(self.atoms) fod1forces = np.zeros_like(fod1.get_positions()) fod2forces = np.zeros_like(fod2.get_positions()) totalforces.extend(fod1forces) totalforces.extend(fod2forces) totalforces = np.array(totalforces) # pyscf gives the gradient not the force totalforces = -1 * totalforces if self.mode == 'flosic-os' or self.mode == 'flosic-scf': # mode for FOD only optimization (nuclei fixed) if self.results['fodforces'] is None: fodforces = self.get_fodforces(self.atoms) fodforces = self.results['fodforces'] # fix nuclei with zeroing the forces forces = forces forces = forces.tolist() totalforces = [] totalforces.extend(forces) totalforces.extend(fodforces) totalforces = np.array(totalforces) if self.mode == 'both': # mode for both (nuclei+fods) optimzation if self.results['fodforces'] is None: fodforces = self.get_fodforces(self.atoms) fodforces = self.results['fodforces'] forces = forces.tolist() totalforces = [] totalforces.extend(forces) totalforces.extend(fodforces) totalforces = np.array(totalforces) return totalforces