def test_rb_torsions_vs_foyer(self, ethanol_with_rb_torsions): # Given that these force constants are copied from Foyer's OPLS-AA file, # compare to processing through the current MoSDeF pipeline import foyer import mbuild comp = mbuild.load("CC", smiles=True) comp.xyz = ethanol_with_rb_torsions.positions.m_as(unit.nanometer) ff = foyer.Forcefield(name="oplsaa") from_foyer = ff.apply(comp) from_foyer.box = [40, 40, 40, 90, 90, 90] from_foyer.save("from_foyer.top") from_foyer.save("from_foyer.gro") rb_torsion_energy_from_foyer = _run_gmx_energy( top_file="from_foyer.top", gro_file="from_foyer.gro", mdp_file=_get_mdp_file("default"), ).energies["Torsion"] # GROMACS vs. OpenMM was already compared, so just use one omm = get_gromacs_energies(ethanol_with_rb_torsions, decimal=3).energies[ "Torsion" ] assert (omm - rb_torsion_energy_from_foyer).m_as(kj_mol) < 1e-6
def test_interchange_energies(self, molecule_path, get_interchanges, oplsaa): if "ethanol" in molecule_path or "adamantane" in molecule_path: pytest.skip("Foyer/ParmEd bug with this molecule") openff_interchange, pmd_structure = get_interchanges(molecule_path) parameterized_pmd_structure = oplsaa.apply(pmd_structure) openff_energy = get_gromacs_energies( openff_interchange, decimal=3, mdp="cutoff_hbonds" ) parameterized_pmd_structure.save("from_foyer.gro") parameterized_pmd_structure.save("from_foyer.top") through_foyer = _run_gmx_energy( top_file="from_foyer.top", gro_file="from_foyer.gro", mdp_file=_get_mdp_file("cutoff_hbonds"), ) # TODO: Revisit after https://github.com/mosdef-hub/foyer/issues/431 openff_energy.compare( through_foyer, custom_tolerances={ "Bond": 1e-3 * unit.kilojoule / unit.mole, "Angle": 1e-3 * unit.kilojoule / unit.mole, "Nonbonded": 1e-3 * unit.kilojoule / unit.mole, "Torsion": 1e-3 * unit.kilojoule / unit.mole, "vdW": 5 * unit.kilojoule / unit.mole, "Electrostatics": 1 * unit.kilojoule / unit.mole, }, )
def test_cutoff_electrostatics(): ion_ff = ForceField(get_test_file_path("ions.offxml")) ions = Topology.from_molecules([ Molecule.from_smiles("[#3]"), Molecule.from_smiles("[#17]"), ]) out = Interchange.from_smirnoff(ion_ff, ions) out.box = [4, 4, 4] * unit.nanometer gmx = [] lmp = [] for d in np.linspace(0.75, 0.95, 5): positions = np.zeros((2, 3)) * unit.nanometer positions[1, 0] = d * unit.nanometer out.positions = positions out["Electrostatics"].method = "cutoff" gmx.append( get_gromacs_energies(out, mdp="auto").energies["Electrostatics"].m) lmp.append( get_lammps_energies(out).energies["Electrostatics"].m_as( unit.kilojoule / unit.mol)) assert np.sum(np.sqrt(np.square(np.asarray(lmp) - np.asarray(gmx)))) < 1e-3
def test_rb_torsions(self, ethanol_with_rb_torsions): omm = get_openmm_energies(ethanol_with_rb_torsions, round_positions=3).energies[ "Torsion" ] gmx = get_gromacs_energies(ethanol_with_rb_torsions, decimal=3).energies[ "Torsion" ] assert (gmx - omm).m_as(kj_mol) < 1e-6
def test_parmed_roundtrip(self): original = pmd.load_file(get_test_file_path("ALA_GLY/ALA_GLY.top")) gro = pmd.load_file(get_test_file_path("ALA_GLY/ALA_GLY.gro")) original.box = gro.box original.positions = gro.positions openff_sys = Interchange._from_parmed(original) openff_sys.topology.mdtop = md.Topology.from_openmm(gro.topology) # Some sanity checks, including that residues are stored ... assert openff_sys.topology.mdtop.n_atoms == 29 # TODO: Assert number of topology molecules after refactor assert openff_sys.topology.mdtop.n_residues == 4 # ... and written out openff_sys.to_gro("has_residues.gro", writer="internal") assert len(pmd.load_file("has_residues.gro").residues) == 4 roundtrip = openff_sys._to_parmed() roundtrip.save("conv.gro", overwrite=True) roundtrip.save("conv.top", overwrite=True) original_energy = _run_gmx_energy( top_file=get_test_file_path("ALA_GLY/ALA_GLY.top"), gro_file=get_test_file_path("ALA_GLY/ALA_GLY.gro"), mdp_file=_get_mdp_file("cutoff_hbonds"), ) internal_energy = get_gromacs_energies(openff_sys, mdp="cutoff_hbonds") roundtrip_energy = _run_gmx_energy( top_file="conv.top", gro_file="conv.gro", mdp_file=_get_mdp_file("cutoff_hbonds"), ) # Differences in bond energies appear to be related to ParmEd's rounding # of the force constant and equilibrium bond length original_energy.compare(internal_energy) internal_energy.compare( roundtrip_energy, custom_tolerances={ "Bond": 0.02 * omm_unit.kilojoule_per_mole, }, ) original_energy.compare( roundtrip_energy, custom_tolerances={ "Bond": 0.02 * omm_unit.kilojoule_per_mole, }, )
def test_ethanol_energies(self, oplsaa_interchange_ethanol): from openff.interchange.tests.energy_tests.test_energies import ( get_gromacs_energies, ) gmx_energies = get_gromacs_energies(oplsaa_interchange_ethanol) omm_energies = get_openmm_energies(oplsaa_interchange_ethanol) gmx_energies.compare( omm_energies, custom_tolerances={ "vdW": 12.0 * unit.kilojoule / unit.mole, "Electrostatics": 12.0 * unit.kilojoule / unit.mole, }, )
def test_liquid_argon(): argon = Molecule.from_smiles("[#18]") pdbfile = app.PDBFile(get_test_file_path("packed-argon.pdb")) top = Topology.from_openmm(pdbfile.topology, unique_molecules=[argon]) argon_ff = ForceField(get_test_file_path("argon.offxml")) out = Interchange.from_smirnoff(argon_ff, top) out.positions = pdbfile.positions omm_energies = get_openmm_energies(out) gmx_energies = get_gromacs_energies( out, mdp="auto", writer="internal", ) omm_energies.compare( gmx_energies, custom_tolerances={ "vdW": 0.008 * simtk_unit.kilojoule_per_mole, }, ) argon_ff_no_switch = deepcopy(argon_ff) argon_ff_no_switch["vdW"].switch_width *= 0 out_no_switch = Interchange.from_smirnoff(argon_ff_no_switch, top) out_no_switch.positions = pdbfile.positions lmp_energies = get_lammps_energies(out_no_switch) omm_energies.compare( lmp_energies, custom_tolerances={ "vdW": 10.5 * simtk_unit.kilojoule_per_mole, }, )
def test_water_dimer(): tip3p = ForceField(get_test_file_path("tip3p.offxml")) water = Molecule.from_smiles("O") top = Topology.from_molecules(2 * [water]) top.mdtop = md.Topology.from_openmm(top.to_openmm()) pdbfile = openmm.app.PDBFile(get_test_file_path("water-dimer.pdb")) positions = pdbfile.positions openff_sys = Interchange.from_smirnoff(tip3p, top) openff_sys.positions = positions openff_sys.box = [10, 10, 10] * unit.nanometer omm_energies = get_openmm_energies( openff_sys, hard_cutoff=True, electrostatics=False, ) toolkit_energies = _get_openmm_energies( tip3p.create_openmm_system(top), openff_sys.box, openff_sys.positions, hard_cutoff=True, electrostatics=False, ) omm_energies.compare(toolkit_energies) # TODO: Fix GROMACS energies by handling SETTLE constraints # gmx_energies, _ = get_gromacs_energies(openff_sys) # compare_gromacs_openmm(omm_energies=omm_energies, gmx_energies=gmx_energies) openff_sys["Electrostatics"].method = "cutoff" omm_energies_cutoff = get_gromacs_energies(openff_sys) lmp_energies = get_lammps_energies(openff_sys) lmp_energies.compare(omm_energies_cutoff)
def test_gmx_14_energies_exist(): # TODO: Make sure 1-4 energies are accurate, not just existent # Use a molecule with only one 1-4 interaction, and # make it between heavy atoms because H-H 1-4 are weak mol = Molecule.from_smiles("ClC#CCl") mol.name = "HPER" mol.generate_conformers(n_conformers=1) parsley = ForceField("openff-1.0.0.offxml") out = Interchange.from_smirnoff(parsley, mol.to_topology()) out.positions = mol.conformers[0] # Put this molecule in a large box with cut-off electrostatics # to prevent it from interacting with images of itself out.box = [40, 40, 40] out["Electrostatics"].method = "cutoff" gmx_energies = get_gromacs_energies(out) # The only possible non-bonded interactions should be from 1-4 intramolecular interactions assert gmx_energies.energies["vdW"].m != 0.0 assert gmx_energies.energies["Electrostatics"].m != 0.0
def test_energies_single_mol(constrained, mol_smi): import mbuild as mb mol = Molecule.from_smiles(mol_smi) mol.generate_conformers(n_conformers=1) mol.name = "FOO" top = mol.to_topology() top.box_vectors = None # [10, 10, 10] * simtk_unit.nanometer if constrained: parsley = ForceField("openff-1.0.0.offxml") else: parsley = ForceField("openff_unconstrained-1.0.0.offxml") off_sys = Interchange.from_smirnoff(parsley, top) off_sys.handlers["Electrostatics"].method = "cutoff" mol.to_file("out.xyz", file_format="xyz") compound: mb.Compound = mb.load("out.xyz") packed_box: mb.Compound = mb.fill_box(compound=compound, n_compounds=1, box=mb.Box(lengths=[10, 10, 10])) positions = packed_box.xyz * unit.nanometer off_sys.positions = positions # Compare directly to toolkit's reference implementation omm_energies = get_openmm_energies(off_sys, round_positions=8) omm_reference = parsley.create_openmm_system(top) reference_energies = _get_openmm_energies( omm_sys=omm_reference, box_vectors=off_sys.box, positions=off_sys.positions, round_positions=8, ) omm_energies.compare(reference_energies) mdp = "cutoff_hbonds" if constrained else "auto" # Compare GROMACS writer and OpenMM export gmx_energies = get_gromacs_energies(off_sys, mdp=mdp) custom_tolerances = { "Bond": 2e-5 * simtk_unit.kilojoule_per_mole, "Electrostatics": 2 * simtk_unit.kilojoule_per_mole, "vdW": 2 * simtk_unit.kilojoule_per_mole, "Nonbonded": 2 * simtk_unit.kilojoule_per_mole, "Angle": 1e-4 * simtk_unit.kilojoule_per_mole, } gmx_energies.compare( omm_energies, custom_tolerances=custom_tolerances, ) if not constrained: other_energies = get_openmm_energies( off_sys, round_positions=8, hard_cutoff=True, electrostatics=True, ) lmp_energies = get_lammps_energies(off_sys) custom_tolerances = { "vdW": 5.0 * simtk_unit.kilojoule_per_mole, "Electrostatics": 5.0 * simtk_unit.kilojoule_per_mole, } lmp_energies.compare(other_energies, custom_tolerances=custom_tolerances)
def test_packmol_boxes(toolkit_file_path): # TODO: Isolate a set of systems here instead of using toolkit data # TODO: Fix nonbonded energy differences from openff.toolkit.utils import get_data_file_path pdb_file_path = get_data_file_path(toolkit_file_path) pdbfile = openmm.app.PDBFile(pdb_file_path) ethanol = Molecule.from_smiles("CCO") cyclohexane = Molecule.from_smiles("C1CCCCC1") omm_topology = pdbfile.topology off_topology = OFFBioTop.from_openmm( omm_topology, unique_molecules=[ethanol, cyclohexane]) off_topology.mdtop = md.Topology.from_openmm(omm_topology) parsley = ForceField("openff_unconstrained-1.0.0.offxml") off_sys = Interchange.from_smirnoff(parsley, off_topology) off_sys.box = np.asarray( pdbfile.topology.getPeriodicBoxVectors().value_in_unit( simtk_unit.nanometer)) off_sys.positions = pdbfile.positions sys_from_toolkit = parsley.create_openmm_system(off_topology) omm_energies = get_openmm_energies(off_sys, hard_cutoff=True, electrostatics=False) reference = _get_openmm_energies( sys_from_toolkit, off_sys.box, off_sys.positions, hard_cutoff=True, electrostatics=False, ) omm_energies.compare( reference, custom_tolerances={ "Electrostatics": 2e-2 * simtk_unit.kilojoule_per_mole, }, ) # custom_tolerances={"HarmonicBondForce": 1.0} # Compare GROMACS writer and OpenMM export gmx_energies = get_gromacs_energies(off_sys, electrostatics=False) omm_energies_rounded = get_openmm_energies( off_sys, round_positions=8, hard_cutoff=True, electrostatics=False, ) omm_energies_rounded.compare( other=gmx_energies, custom_tolerances={ "Angle": 1e-2 * simtk_unit.kilojoule_per_mole, "Torsion": 1e-2 * simtk_unit.kilojoule_per_mole, "Electrostatics": 3200 * simtk_unit.kilojoule_per_mole, }, )