def test_forces(self): for calc in [EAM('Au-Grochola-JCP05.eam.alloy')]: a = io.read('Au_923.xyz') a.center(vacuum=10.0) a.set_calculator(calc) f = a.get_forces() for i in range(9): atindex = i * 100 fn = [ numeric_force(a, atindex, 0, self.disp), numeric_force(a, atindex, 1, self.disp), numeric_force(a, atindex, 2, self.disp) ] self.assertArrayAlmostEqual(f[atindex], fn, tol=self.tol)
def calculate_numerical_forces(self, atoms, d=0.001): """Calculate numerical forces using finite difference. All atoms will be displaced by +d and -d in all directions.""" return np.array([[numeric_force(atoms, a, i, d) for i in range(3)] for a in range(len(atoms))])
def calculate_numerical_forces(self, atoms, d=0.001): """Calculate numerical forces using finite difference. All atoms will be displaced by +d and -d in all directions.""" from ase.calculators.test import numeric_force return np.array([[numeric_force(atoms, a, i, d) for i in range(3)] for a in range(len(atoms))])
def get_forces(self, atoms=None): """Get finite-difference forces""" if atoms is None: atoms = self.atoms if self.calculation_required(atoms, ['F_av']): atoms.set_calculator(self) # do the ground state calculation to set all # ranks to the same density to start with self.calculator.calculate(atoms) world = self.parallel['world'] txt = self.txt if world.rank > 0: txt = sys.stdout mycomm = self.parallel['mycomm'] ncalcs = self.parallel['ncalcs'] icalc = self.parallel['icalc'] F_av = np.zeros((len(atoms), 3)) i = 0 for ia, a in enumerate(self.atoms): for ic in range(3): # print "ncalcs", ncalcs, "i", i, "icalc",icalc if (i % ncalcs) == icalc: F_av[ia, ic] = numeric_force( atoms, ia, ic, self.d) / mycomm.size prnt('# rank', world.rank, '-> force', (str(ia) + 'xyz'[ic]), file=txt) i += 1 energy = np.array([0.]) # array needed for world.sum() if (i % ncalcs) == icalc: self.energy = None energy[0] = self.get_potential_energy(atoms) / mycomm.size prnt('# rank', world.rank, '-> energy', energy[0] * mycomm.size, file=txt) self.set_positions(atoms) world.sum(F_av) world.sum(energy) self.energy = energy[0] self.F_av = F_av if self.txt: prnt('Excited state forces in eV/Ang:', file=self.txt) symbols = self.atoms.get_chemical_symbols() for a, symbol in enumerate(symbols): prnt(('%3d %-2s %10.5f %10.5f %10.5f' % ((a, symbol) + tuple(self.F_av[a]))), file=self.txt) return self.F_av
def get_forces(self, atoms=None): """Get finite-difference forces""" if atoms is None: atoms = self.atoms if self.calculation_required(atoms, ['F_av']): atoms.set_calculator(self) # do the ground state calculation to set all # ranks to the same density to start with self.calculator.calculate(atoms) world = self.parallel['world'] txt = self.txt if world.rank > 0: txt = sys.stdout mycomm = self.parallel['mycomm'] ncalcs = self.parallel['ncalcs'] icalc = self.parallel['icalc'] F_av = np.zeros((len(atoms), 3)) i = 0 for ia, a in enumerate(self.atoms): for ic in range(3): if (i % ncalcs) == icalc: F_av[ia, ic] = numeric_force( atoms, ia, ic, self.d) / mycomm.size prnt('# rank', world.rank, '-> force', (str(ia) + 'xyz'[ic]), file=txt) i += 1 energy = np.array([0.]) # array needed for world.sum() if (i % ncalcs) == icalc: self.energy = None energy[0] = self.get_potential_energy(atoms) / mycomm.size prnt('# rank', world.rank, '-> energy', energy[0] * mycomm.size, file=txt) self.set_positions(atoms) world.sum(F_av) world.sum(energy) self.energy = energy[0] self.F_av = F_av if self.txt: prnt('Excited state forces in eV/Ang:', file=self.txt) symbols = self.atoms.get_chemical_symbols() for a, symbol in enumerate(symbols): prnt(('%3d %-2s %10.5f %10.5f %10.5f' % ((a, symbol) + tuple(self.F_av[a]))), file=self.txt) return self.F_av
def get_numeric_force(atoms, eps): fn = torch.zeros((len(atoms), 3)) for i in range(len(atoms)): for j in range(3): fn[i, j] = numeric_force(atoms, i, j, eps) return fn
from ase.calculators.test import numeric_force from gpaw import GPAW, Mixer from gpaw.test import equal a = 4.0 n = 16 atoms = Atoms([Atom('H', [1.234, 2.345, 3.456])], cell=(a, a, a), pbc=True) calc = GPAW(nbands=1, gpts=(n, n, n), txt=None, mixer=Mixer(0.25, 3, 1), convergence={'energy': 1e-7}) atoms.set_calculator(calc) e1 = atoms.get_potential_energy() niter1 = calc.get_number_of_iterations() f1 = atoms.get_forces()[0] for i in range(3): f2i = numeric_force(atoms, 0, i) print f1[i]-f2i equal(f1[i], f2i, 0.00025) energy_tolerance = 0.00006 force_tolerance = 0.0001 niter_tolerance = 0 equal(e1, -0.531042, energy_tolerance) f1_ref = [-0.291893, -0.305174, -0.35329] for i in range(3): equal(f1[i], f1_ref[i], force_tolerance) assert 34 <= niter1 <= 35, niter1
(0.75, 0.25, 0.75), (0.75, 0.75, 0.25)], pbc=True) bulk.set_cell((a, a, a), scale_atoms=True) n = 20 calc = GPAW(gpts=(n, n, n), nbands=8 * 3, occupations=FermiDirac(width=0.01), poissonsolver=PoissonSolver(nn='M', relax='J'), kpts=(2, 2, 2), convergence={'energy': 1e-7}) bulk.set_calculator(calc) f1 = bulk.get_forces()[0, 2] e1 = bulk.get_potential_energy() niter1 = calc.get_number_of_iterations() f2 = numeric_force(bulk, 0, 2) print f1, f2, f1 - f2 equal(f1, f2, 0.005) # Volume per atom: vol = a**3 / 8 de = calc.get_electrostatic_corrections() / vol print de assert abs(de[0] - -2.190) < 0.001 print e1, f1, niter1 energy_tolerance = 0.00025 force_tolerance = 0.0001 niter_tolerance = 0 equal(e1, -46.6596470348, energy_tolerance) # svnversion 5252 equal(f1, -1.38242356123, force_tolerance) # svnversion 5252
pbc=True) bulk.set_cell((a, a, a), scale_atoms=True) n = 20 calc = GPAW(gpts=(n, n, n), nbands=8*3, occupations=FermiDirac(width=0.01), poissonsolver=PoissonSolver(nn='M', relax='J'), kpts=(2, 2, 2), convergence={'energy': 1e-7} ) bulk.set_calculator(calc) f1 = bulk.get_forces()[0, 2] e1 = bulk.get_potential_energy() niter1 = calc.get_number_of_iterations() f2 = numeric_force(bulk, 0, 2) print f1,f2,f1-f2 equal(f1, f2, 0.005) # Volume per atom: vol = a**3 / 8 de = calc.get_electrostatic_corrections() / vol print de assert abs(de[0] - -2.190) < 0.001 print e1, f1, niter1 energy_tolerance = 0.00025 force_tolerance = 0.0001 niter_tolerance = 0 equal(e1, -46.6596470348, energy_tolerance) # svnversion 5252 equal(f1, -1.38242356123, force_tolerance) # svnversion 5252
# High-level test: lih = Atoms('LiH') lih[1].y += 1.64 lih.center(vacuum=3) pos = lih.cell.sum(axis=0) print(pos) pc = PointChargePotential([-1.0], [pos]) lih.calc = GPAW(external=pc, txt='lih-pc.txt') f1 = lih.get_forces() fpc1 = pc.get_forces(lih.calc) print(fpc1) print(fpc1 + f1.sum(0)) f2 = [[numeric_force(lih, a, v) for v in range(3)] for a in range(2)] print(f1) print(f1 - f2) assert abs(f1 - f2).max() < 2e-3 x = 0.0001 for v in range(3): pos[v] += x pc.set_positions([pos]) ep = lih.get_potential_energy() pos[v] -= 2 * x pc.set_positions([pos]) em = lih.get_potential_energy() pos[v] += x pc.set_positions([pos]) error = (em - ep) / (2 * x) - fpc1[0, v]
print(F_ac) print() print('Reference result') print(F_ac_ref) print() print('Error') print(err_ac) print() print('Max error') print(err) # ASE uses dx = [+|-] 0.001 by default, # error should be around 2e-3. In fact 4e-3 would probably be acceptable assert err < 3e-3 # Set boolean to run new FD check fd = not not False if fd: from ase.calculators.test import numeric_force F_ac_fd = np.array([[numeric_force(system, a, i) for i in range(3)] for a in range(len(system))]) print('Self-consistent forces') print(F_ac) print('FD') print(F_ac_fd) print(repr(F_ac_fd)) print(F_ac - F_ac_fd, np.abs(F_ac - F_ac_fd).max()) assert np.abs(F_ac - F_ac_fd).max() < 4e-3