def test_qmmm_acn(): import numpy as np import ase.units as units from ase import Atoms from ase.calculators.acn import (ACN, m_me, r_cn, r_mec, sigma_me, sigma_c, sigma_n, epsilon_me, epsilon_c, epsilon_n) from ase.calculators.qmmm import SimpleQMMM, LJInteractionsGeneral, EIQMMM from ase.constraints import FixLinearTriatomic from ase.optimize import BFGS # From https://www.sciencedirect.com/science/article/pii/S0166128099002079 eref = 4.9 * units.kcal / units.mol dref = 3.368 aref = 79.1 sigma = np.array([sigma_me, sigma_c, sigma_n]) epsilon = np.array([epsilon_me, epsilon_c, epsilon_n]) inter = LJInteractionsGeneral(sigma, epsilon, sigma, epsilon, 3) for calc in [ACN(), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN()), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN(), vacuum=3.0), EIQMMM([0, 1, 2], ACN(), ACN(), inter), EIQMMM([0, 1, 2], ACN(), ACN(), inter, vacuum=3.0), EIQMMM([3, 4, 5], ACN(), ACN(), inter, vacuum=3.0)]: dimer = Atoms('CCNCCN', [(-r_mec, 0, 0), (0, 0, 0), (r_cn, 0, 0), (r_mec, 3.7, 0), (0, 3.7, 0), (-r_cn, 3.7, 0)]) masses = dimer.get_masses() masses[::3] = m_me dimer.set_masses(masses) dimer.calc = calc fixd = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)]) dimer.set_constraint(fixd) opt = BFGS(dimer, maxstep=0.04, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.001, steps=1000) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(1, 4) a0 = dimer.get_angle(2, 1, 4) fmt = '{0:>25}: {1:.3f} {2:.3f} {3:.1f}' print(fmt.format(calc.name, -e0, d0, a0)) assert abs(e0 + eref) < 0.013 assert abs(d0 - dref) < 0.224 assert abs(a0 - aref) < 2.9 print(fmt.format('reference', eref, dref, aref))
def test_rattle_linear(): """Test RATTLE and QM/MM for rigid linear acetonitrile.""" import numpy as np from ase import Atoms from ase.calculators.acn import (ACN, m_me, r_cn, r_mec, sigma_me, sigma_c, sigma_n, epsilon_me, epsilon_c, epsilon_n) from ase.calculators.qmmm import SimpleQMMM, EIQMMM, LJInteractionsGeneral from ase.md.verlet import VelocityVerlet from ase.constraints import FixLinearTriatomic import ase.units as units sigma = np.array([sigma_me, sigma_c, sigma_n]) epsilon = np.array([epsilon_me, epsilon_c, epsilon_n]) i = LJInteractionsGeneral(sigma, epsilon, sigma, epsilon, 3) for calc in [ ACN(), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN()), EIQMMM([0, 1, 2], ACN(), ACN(), i) ]: dimer = Atoms('CCNCCN', [(-r_mec, 0, 0), (0, 0, 0), (r_cn, 0, 0), (r_mec, 3.7, 0), (0, 3.7, 0), (-r_cn, 3.7, 0)]) masses = dimer.get_masses() masses[::3] = m_me dimer.set_masses(masses) fixd = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)]) dimer.set_constraint(fixd) dimer.calc = calc d1 = dimer[:3].get_all_distances() d2 = dimer[3:].get_all_distances() e = dimer.get_potential_energy() md = VelocityVerlet(dimer, 2.0 * units.fs, trajectory=calc.name + '.traj', logfile=calc.name + '.log', loginterval=20) md.run(100) de = dimer.get_potential_energy() - e assert np.all(abs(dimer[:3].get_all_distances() - d1) < 1e-10) assert np.all(abs(dimer[3:].get_all_distances() - d2) < 1e-10) assert abs(de - -0.005) < 0.001
sig2, eps2, # arrays for TIP4P from earlier rc=7.5) mmatoms.calc = calc mmatoms.calc.initialize(mmatoms) # LJ arrays for the 'QM' subsystem sig_qm = np.array([sigNa, sigNa]) eps_qm = np.array([epsNa, epsNa]) # For the MM subsystem, tuple of arrays (counterion, water) sig_mm = (np.array([sigNa]), sig2) eps_mm = (np.array([epsNa]), eps2) lj = LJInteractionsGeneral(sig_qm, eps_qm, sig_mm, eps_mm, 2) ecomb, fcomb1, fcomb2 = lj.calculate(faux_qm, mmatoms, np.array([0, 0, 0])) # This should give the same result as if not using CombineMM, on a sum # of these systems: # A: All the Na atoms in the 'QM' region # B: LJInteractions between the 'QM' Na and the 'MM' Na # C: -LJinteractions between the 'MM' Na and the 'MM' Water # A: mmatoms = dimer mmatoms.calc = [] sig_qm = np.concatenate((sig_qm, sig_qm)) eps_qm = np.concatenate((eps_qm, eps_qm)) sig_mm = sig_mm[1]
def test_qmmm_tip4p(): from math import cos, sin, pi import numpy as np #import matplotlib.pyplot as plt import ase.units as units from ase import Atoms from ase.calculators.tip4p import TIP4P, epsilon0, sigma0, rOH, angleHOH from ase.calculators.qmmm import (SimpleQMMM, EIQMMM, LJInteractions, LJInteractionsGeneral) from ase.constraints import FixInternals from ase.optimize import BFGS r = rOH a = angleHOH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.24 * units.kcal / units.mol dexp = 2.75 aexp = 46 D = np.linspace(2.5, 3.5, 30) i = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) # General LJ interaction object sigma_mm = np.array([sigma0, 0, 0]) epsilon_mm = np.array([epsilon0, 0, 0]) sigma_qm = np.array([sigma0, 0, 0]) epsilon_qm = np.array([epsilon0, 0, 0]) ig = LJInteractionsGeneral(sigma_qm, epsilon_qm, sigma_mm, epsilon_mm, 3) for calc in [ TIP4P(), SimpleQMMM([0, 1, 2], TIP4P(), TIP4P(), TIP4P()), SimpleQMMM([0, 1, 2], TIP4P(), TIP4P(), TIP4P(), vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), i), EIQMMM([3, 4, 5], TIP4P(), TIP4P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), ig), EIQMMM([3, 4, 5], TIP4P(), TIP4P(), ig, vacuum=3.0), EIQMMM([0, 1, 2], TIP4P(), TIP4P(), ig, vacuum=3.0) ]: dimer = Atoms('OH2OH2', [(0, 0, 0), (r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0)]) dimer.calc = calc E = [] F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[3, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) #plt.plot(D, E) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() assert error < 0.01 dimer.constraints = FixInternals(bonds=[(r, (0, 1)), (r, (0, 2)), (r, (3, 4)), (r, (3, 5))], angles=[(a, (2, 0, 1)), (a, (5, 3, 4))]) opt = BFGS(dimer, maxstep=0.04, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(0, 3) R = dimer.positions v1 = R[2] - R[3] v2 = R[3] - (R[5] + R[4]) / 2 a0 = np.arccos( np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>23}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0)) assert abs(e0 + eexp) < 0.002 assert abs(d0 - dexp) < 0.006 assert abs(a0 - aexp) < 2.5 print(fmt.format('reference', 9.999, eexp, dexp, aexp))
"""Test RATTLE and QM/MM for rigid linear acetonitrile.""" import numpy as np from ase import Atoms from ase.calculators.acn import (ACN, m_me, r_cn, r_mec, sigma_me, sigma_c, sigma_n, epsilon_me, epsilon_c, epsilon_n) from ase.calculators.qmmm import SimpleQMMM, EIQMMM, LJInteractionsGeneral from ase.md.verlet import VelocityVerlet from ase.constraints import FixLinearTriatomic import ase.units as units sigma = np.array([sigma_me, sigma_c, sigma_n]) epsilon = np.array([epsilon_me, epsilon_c, epsilon_n]) i = LJInteractionsGeneral(sigma, epsilon, sigma, epsilon, 3) for calc in [ ACN(), SimpleQMMM([0, 1, 2], ACN(), ACN(), ACN()), EIQMMM([0, 1, 2], ACN(), ACN(), i) ]: dimer = Atoms('CCNCCN', [(-r_mec, 0, 0), (0, 0, 0), (r_cn, 0, 0), (r_mec, 3.7, 0), (0, 3.7, 0), (-r_cn, 3.7, 0)]) masses = dimer.get_masses() masses[::3] = m_me dimer.set_masses(masses) fixd = FixLinearTriatomic(triples=[(0, 1, 2), (3, 4, 5)])
# From http://dx.doi.org/10.1063/1.445869 eexp = 6.50 * units.kcal / units.mol dexp = 2.74 aexp = 27 D = np.linspace(2.5, 3.5, 30) i = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) # General LJ interaction object sigma_mm = np.array([0, 0, sigma0]) epsilon_mm = np.array([0, 0, epsilon0]) sigma_qm = np.array([0, 0, sigma0]) epsilon_qm = np.array([0, 0, epsilon0]) ig = LJInteractionsGeneral(sigma_qm, epsilon_qm, sigma_mm, epsilon_mm, 3) for calc in [TIP3P(), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P()), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P(), vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), ig, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig, vacuum=3.0)]: dimer = Atoms('H2OH2O', [(r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0),
def test_qmmm(testdir): r = rOH a = angleHOH * pi / 180 # From https://doi.org/10.1063/1.445869 eexp = 6.50 * units.kcal / units.mol dexp = 2.74 aexp = 27 D = np.linspace(2.5, 3.5, 30) i = LJInteractions({('O', 'O'): (epsilon0, sigma0)}) # General LJ interaction object sigma_mm = np.array([0, 0, sigma0]) epsilon_mm = np.array([0, 0, epsilon0]) sigma_qm = np.array([0, 0, sigma0]) epsilon_qm = np.array([0, 0, epsilon0]) ig = LJInteractionsGeneral(sigma_qm, epsilon_qm, sigma_mm, epsilon_mm, 3) for calc in [ TIP3P(), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P()), SimpleQMMM([0, 1, 2], TIP3P(), TIP3P(), TIP3P(), vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), i, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig), EIQMMM([3, 4, 5], TIP3P(), TIP3P(), ig, vacuum=3.0), EIQMMM([0, 1, 2], TIP3P(), TIP3P(), ig, vacuum=3.0) ]: dimer = Atoms('H2OH2O', [(r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0), (0, 0, 0)]) dimer.calc = calc E = [] F = [] for d in D: dimer.positions[3:, 0] += d - dimer.positions[5, 0] E.append(dimer.get_potential_energy()) F.append(dimer.get_forces()) F = np.array(F) F1 = np.polyval(np.polyder(np.polyfit(D, E, 7)), D) F2 = F[:, :3, 0].sum(1) error = abs(F1 - F2).max() assert error < 0.01 dimer.constraints = FixInternals(bonds=[(r, (0, 2)), (r, (1, 2)), (r, (3, 5)), (r, (4, 5))], angles_deg=[ (np.degrees(a), (0, 2, 1)), (np.degrees(a), (3, 5, 4)) ]) opt = GPMin(dimer, trajectory=calc.name + '.traj', logfile=calc.name + 'd.log') opt.run(0.01) e0 = dimer.get_potential_energy() d0 = dimer.get_distance(2, 5) R = dimer.positions v1 = R[1] - R[5] v2 = R[5] - (R[3] + R[4]) / 2 a0 = np.arccos( np.dot(v1, v2) / (np.dot(v1, v1) * np.dot(v2, v2))**0.5) / np.pi * 180 fmt = '{0:>20}: {1:.3f} {2:.3f} {3:.3f} {4:.1f}' print(fmt.format(calc.name, -min(E), -e0, d0, a0)) assert abs(e0 + eexp) < 0.002 assert abs(d0 - dexp) < 0.01 assert abs(a0 - aexp) < 4 print(fmt.format('reference', 9.999, eexp, dexp, aexp))
def test_combine_mm(): """Test CombineMM forces by combining tip3p and tip4p with them selves, and by combining tip3p with tip4p and testing against numerical forces. Also test LJInterationsGeneral with CombineMM """ from math import cos, sin, pi import numpy as np from ase import Atoms from ase import units from ase.calculators.counterions import AtomicCounterIon as ACI from ase.calculators.combine_mm import CombineMM from ase.calculators.qmmm import LJInteractionsGeneral from ase.calculators.tip3p import TIP3P, rOH, angleHOH from ase.calculators.tip4p import TIP4P from ase.calculators.tip3p import epsilon0 as eps3 from ase.calculators.tip3p import sigma0 as sig3 from ase.calculators.tip4p import epsilon0 as eps4 from ase.calculators.tip4p import sigma0 as sig4 def make_atoms(): r = rOH a = angleHOH * pi / 180 dimer = Atoms('H2OH2O', [(r * cos(a), 0, r * sin(a)), (r, 0, 0), (0, 0, 0), (r * cos(a / 2), r * sin(a / 2), 0), (r * cos(a / 2), -r * sin(a / 2), 0), (0, 0, 0)]) dimer = dimer[[2, 0, 1, 5, 3, 4]] # put O-O distance in the cutoff range dimer.positions[3:, 0] += 2.8 return dimer dimer = make_atoms() rc = 3.0 for (TIPnP, (eps, sig), nm) in zip([TIP3P, TIP4P], ((eps3, sig3), (eps4, sig4)), [3, 3]): dimer.calc = TIPnP(rc=rc, width=1.0) F1 = dimer.get_forces() sigma = np.array([sig, 0, 0]) epsilon = np.array([eps, 0, 0]) dimer.calc = CombineMM([0, 1, 2], nm, nm, TIPnP(rc=rc, width=1.0), TIPnP(rc=rc, width=1.0), sigma, epsilon, sigma, epsilon, rc=rc, width=1.0) F2 = dimer.get_forces() dF = F1 - F2 print(TIPnP) print(dF) assert abs(dF).max() < 1e-8 # Also check a TIP3P/TIP4P combination against numerical forces: eps1 = np.array([eps3, 0, 0]) sig1 = np.array([sig3, 0, 0]) eps2 = np.array([eps4, 0, 0]) sig2 = np.array([sig4, 0, 0]) dimer.calc = CombineMM([0, 1, 2], 3, 3, TIP3P(rc, 1.0), TIP4P(rc, 1.0), sig1, eps1, sig2, eps2, rc, 1.0) F2 = dimer.get_forces() Fn = dimer.calc.calculate_numerical_forces(dimer, 1e-7) dF = F2 - Fn print('TIP3P/TIP4P') print(dF) assert abs(dF).max() < 1e-8 # LJInteractionsGeneral with CombineMM. # As it is used within EIQMMM, but avoiding long calculations for tests. # Total system is a unit test system comprised of: # 2 Na ions playing the role of a 'QM' subsystem # 2 Na ions as the 'Counterions' # 2 Water molecules dimer.calc = [] faux_qm = Atoms('2Na', positions=np.array([[1.4, -4, 0], [1.4, 4, 0]])) ions = Atoms('2Na', positions=np.array([[1.4, 0, -4], [1.4, 0, 4]])) mmatoms = ions + dimer sigNa = 1.868 * (1.0 / 2.0)**(1.0 / 6.0) * 10 epsNa = 0.00277 * units.kcal / units.mol # ACI for atoms 0 and 1 of the MM subsystem (2 and 3 for the total system) # 1 atom 'per molecule'. The rest is TIP4P, 3 atoms per molecule: calc = CombineMM( [0, 1], 1, 3, ACI(1, epsNa, sigNa), # calc 1 TIP4P(), # calc 2 [sigNa], [epsNa], # LJs for subsystem 1 sig2, eps2, # arrays for TIP4P from earlier rc=7.5) mmatoms.calc = calc mmatoms.calc.initialize(mmatoms) # LJ arrays for the 'QM' subsystem sig_qm = np.array([sigNa, sigNa]) eps_qm = np.array([epsNa, epsNa]) # For the MM subsystem, tuple of arrays (counterion, water) sig_mm = (np.array([sigNa]), sig2) eps_mm = (np.array([epsNa]), eps2) lj = LJInteractionsGeneral(sig_qm, eps_qm, sig_mm, eps_mm, 2) ecomb, fcomb1, fcomb2 = lj.calculate(faux_qm, mmatoms, np.array([0, 0, 0])) # This should give the same result as if not using CombineMM, on a sum # of these systems: # A: All the Na atoms in the 'QM' region # B: LJInteractions between the 'QM' Na and the 'MM' Na # C: -LJinteractions between the 'MM' Na and the 'MM' Water # A: mmatoms = dimer mmatoms.calc = [] sig_qm = np.concatenate((sig_qm, sig_qm)) eps_qm = np.concatenate((eps_qm, eps_qm)) sig_mm = sig_mm[1] eps_mm = eps_mm[1] lj = LJInteractionsGeneral(sig_qm, eps_qm, sig_mm, eps_mm, 4) ea, fa1, fa2 = lj.calculate(faux_qm + ions, mmatoms, np.array([0, 0, 0])) # B: lj = LJInteractionsGeneral(sig_qm[:2], eps_qm[:2], sig_qm[:2], eps_qm[:2], 2, 2) eb, fb1, fb2, = lj.calculate(faux_qm, ions, np.array([0, 0, 0])) # C: lj = LJInteractionsGeneral(sig_qm[:2], eps_qm[:2], sig_mm, eps_mm, 2, 3) ec, fc1, fc2, = lj.calculate(ions, dimer, np.array([0, 0, 0])) assert ecomb - (ea + eb) + ec == 0