def nuc_grad_method(self): from pyscf.grad import uks return uks.Gradients(self)
# 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 optimize(mf)
def test_get_vxc(self): mol = gto.Mole() mol.verbose = 0 mol.atom = [['O', (0., 0., 0.)], [1, (0., -0.757, 0.587)], [1, (0., 0.757, 0.587)]] mol.basis = '631g' mol.charge = -1 mol.spin = 1 mol.build() mf = dft.UKS(mol) mf.xc = 'b3lyp' mf.conv_tol = 1e-12 e0 = mf.scf() g = uks.Gradients(mf) g.grid_response = True g0 = g.kernel() dm0 = mf.make_rdm1() denom = 1 / .00001 * lib.param.BOHR mol1 = gto.Mole() mol1.verbose = 0 mol1.atom = [['O', (0., 0., 0.00001)], [1, (0., -0.757, 0.587)], [1, (0., 0.757, 0.587)]] mol1.basis = '631g' mol1.charge = -1 mol1.spin = 1 mol1.build() mf1 = dft.UKS(mol1) mf1.xc = 'b3lyp' mf1.conv_tol = 1e-12 e1 = mf1.scf() self.assertAlmostEqual((e1 - e0) * denom, g0[0, 2], 3) grids0 = dft.gen_grid.Grids(mol) grids0.atom_grid = (20, 86) grids0.build(with_non0tab=False) grids1 = dft.gen_grid.Grids(mol1) grids1.atom_grid = (20, 86) grids1.build(with_non0tab=False) exc0 = dft.numint.nr_uks(mf._numint, mol, grids0, mf.xc, dm0)[1] exc1 = dft.numint.nr_uks(mf1._numint, mol1, grids1, mf1.xc, dm0)[1] grids0_w = copy.copy(grids0) grids0_w.weights = grids1.weights grids0_c = copy.copy(grids0) grids0_c.coords = grids1.coords exc0_w = dft.numint.nr_uks(mf._numint, mol, grids0_w, mf.xc, dm0)[1] exc0_c = dft.numint.nr_uks(mf._numint, mol1, grids0_c, mf.xc, dm0)[1] dexc_t = (exc1 - exc0) * denom dexc_c = (exc0_c - exc0) * denom dexc_w = (exc0_w - exc0) * denom self.assertAlmostEqual(dexc_t, dexc_c + dexc_w, 4) vxc = uks.get_vxc(mf._numint, mol, grids0, mf.xc, dm0)[1] ev1, vxc1 = uks.get_vxc_full_response(mf._numint, mol, grids0, mf.xc, dm0) p0, p1 = mol.aoslice_by_atom()[0][2:] exc1_approx = numpy.einsum('sxij,sij->x', vxc[:, :, p0:p1], dm0[:, p0:p1]) * 2 exc1_full = numpy.einsum('sxij,sij->x', vxc1[:, :, p0:p1], dm0[:, p0:p1]) * 2 + ev1[0] self.assertAlmostEqual(dexc_t, exc1_approx[2], 3) self.assertAlmostEqual(dexc_t, exc1_full[2], 5)
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