def test_round_trip(self): """ Test ParmEd -> OpenMM round trip with Gromacs system """ # Use DPPC to get RB-torsions tested. Also check that it initially fails # with the CustomNonbondedForce top = load_file(os.path.join(get_fn('12.DPPC'), 'topol.top'), xyz=os.path.join(get_fn('12.DPPC'), 'conf.gro')) self.assertEqual(top.combining_rule, 'lorentz') system = top.createSystem() def bad_system(): return openmm.load_topology(top.topology, system).createSystem() self.assertTrue( openmm.load_topology(top.topology, system).unknown_functional) self.assertRaises(ParmedError, bad_system) for i in range(len(system.getForces())): if isinstance(system.getForce(i), mm.CustomNonbondedForce): system.removeForce(i) break system2 = openmm.load_topology(top.topology, system).createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system2, mm.VerletIntegrator(0.001), CPU) con1.setPositions(top.positions) con2.setPositions(top.positions) ene1 = energy_decomposition(top, con1) ene2 = energy_decomposition(top, con2) self.assertAlmostEqual(ene1['bond'], ene2['bond']) self.assertAlmostEqual(ene1['angle'], ene2['angle']) self.assertAlmostEqual(ene1['dihedral'], ene2['dihedral']) self.assertAlmostEqual(ene1['improper'], ene2['improper']) self.assertAlmostEqual(ene1['rb_torsion'], ene2['rb_torsion']) self.assertAlmostEqual(ene1['nonbonded'], ene2['nonbonded'])
def test_rb_energy_round_trip(tmpdir): """ Make sure that no parameters are lost when reading in RBterms. """ with tmpdir.as_cwd(): # load the molecule and parameterise mol = Ligand.from_file(file_name=get_data("cyclohexane.sdf")) XML().run(molecule=mol, input_files=[get_data("cyclohexane.xml")]) # load the serialised system we extract the parameters from as our reference ref_system = XmlSerializer.deserializeSystem( open("serialised.xml").read()) parm_top = load_topology(mol.to_openmm_topology(), system=ref_system, xyz=mol.openmm_coordinates()) ref_energy = energy_decomposition_system(parm_top, ref_system, platform="Reference") # now we need to build the system from our stored parameters mol.write_parameters(file_name="test.xml") ff = app.ForceField("test.xml") qube_system = ff.createSystem(mol.to_openmm_topology()) with open("qube.xml", "w") as xml_out: xml_out.write(XmlSerializer.serialize(qube_system)) qube_struc = load_topology(mol.to_openmm_topology(), system=qube_system, xyz=mol.openmm_coordinates()) qube_energy = energy_decomposition_system(qube_struc, qube_system, platform="Reference") # compare the decomposed energies of the groups for force_group, energy in ref_energy: for qube_force, qube_e in qube_energy: if force_group == qube_force: assert energy == pytest.approx(qube_e, abs=2e-3)
def test_round_trip(self): """ Test ParmEd -> OpenMM round trip with Gromacs system """ # Use DPPC to get RB-torsions tested. Also check that it initially fails # with the CustomNonbondedForce warnings.filterwarnings('ignore', category=GromacsWarning) top = load_file(os.path.join(get_fn('12.DPPC'), 'topol.top'), xyz=os.path.join(get_fn('12.DPPC'), 'conf.gro')) self.assertEqual(top.combining_rule, 'lorentz') system = top.createSystem() def bad_system(): return openmm.load_topology(top.topology, system).createSystem() warnings.filterwarnings('ignore', category=OpenMMWarning) self.assertTrue( openmm.load_topology(top.topology, system).unknown_functional ) self.assertRaises(ParmedError, bad_system) for i in range(len(system.getForces())): if isinstance(system.getForce(i), mm.CustomNonbondedForce): system.removeForce(i) break system2 = openmm.load_topology(top.topology, system).createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system2, mm.VerletIntegrator(0.001), CPU) con1.setPositions(top.positions) con2.setPositions(top.positions) ene1 = energy_decomposition(top, con1) ene2 = energy_decomposition(top, con2) self.assertAlmostEqual(ene1['bond'], ene2['bond']) self.assertAlmostEqual(ene1['angle'], ene2['angle']) self.assertAlmostEqual(ene1['dihedral'], ene2['dihedral']) self.assertAlmostEqual(ene1['improper'], ene2['improper']) self.assertAlmostEqual(ene1['rb_torsion'], ene2['rb_torsion']) self.assertAlmostEqual(ene1['nonbonded'], ene2['nonbonded'])
def test_load_topology_error(self): """ Test error handling in load_topology """ parm = load_file(get_fn("ash.parm7"), get_fn("ash.rst7")) parm2 = load_file(get_fn("solv2.parm7"), get_fn("solv2.rst7")) self.assertRaises(TypeError, lambda: openmm.load_topology(parm.topology, system=get_fn("integrator.xml"))) self.assertRaises(TypeError, lambda: openmm.load_topology(parm2.topology, system=parm.createSystem())) system = parm.createSystem() system.addForce(mm.CustomTorsionForce("theta**2")) self.assertRaises(exceptions.OpenMMWarning, lambda: openmm.load_topology(parm.topology, system))
def test_offxml_round_trip(tmpdir, openff, molecule): """ Test round tripping offxml parameters through qubekit """ with tmpdir.as_cwd(): mol = Ligand.from_file(get_data(molecule)) offmol = Molecule.from_file(get_data(molecule)) openff.run(mol) mol.to_offxml("test.offxml") # build another openmm system and serialise to compare with deepdiff offxml = ForceField("test.offxml") assert offxml.author == f"QUBEKit_version_{qubekit.__version__}" qubekit_system = offxml.create_openmm_system( topology=offmol.to_topology()) qubekit_xml = xmltodict.parse( openmm.XmlSerializer.serialize(qubekit_system)) with open("qubekit_xml", "w") as output: output.write(openmm.XmlSerializer.serialize(qubekit_system)) openff_system = xmltodict.parse(open("serialised.xml").read()) offxml_diff = DeepDiff( qubekit_xml, openff_system, ignore_order=True, significant_digits=6, ) # the only difference should be in torsions with a 0 barrier height which are excluded from an offxml for item in offxml_diff["iterable_item_removed"].values(): assert item["@k"] == "0" # load both systems and compute the energy qubekit_top = load_topology( mol.to_openmm_topology(), system=qubekit_system, xyz=mol.openmm_coordinates(), ) qubekit_energy = energy_decomposition_system(qubekit_top, qubekit_system, platform="Reference") ref_system = XmlSerializer.deserializeSystem( open("serialised.xml").read()) parm_top = load_topology(mol.to_openmm_topology(), system=ref_system, xyz=mol.openmm_coordinates()) ref_energy = energy_decomposition_system(parm_top, ref_system, platform="Reference") # compare the decomposed energies of the groups for force_group, energy in ref_energy: for qube_force, qube_e in qubekit_energy: if force_group == qube_force: assert energy == pytest.approx(qube_e, abs=2e-3)
def test_load_topology_extra_bonds(self): """ Test loading extra bonds not in Topology """ parm = load_file(get_fn("ash.parm7")) system = parm.createSystem() for force in system.getForces(): if isinstance(force, mm.HarmonicBondForce): force.addBond(0, parm[-1].idx, 1, 500) self.assertRaises(exceptions.OpenMMWarning, lambda: openmm.load_topology(parm.topology, system)) warnings.filterwarnings("ignore", category=exceptions.OpenMMWarning) top = openmm.load_topology(parm.topology, system) self.assertIn(top[-1], top[0].bond_partners) self.assertEqual(len(top.bonds), len(parm.bonds) + 1)
def test_load_topology_extra_bonds(self): """ Test loading extra bonds not in Topology """ parm = load_file(get_fn('ash.parm7')) system = parm.createSystem() for force in system.getForces(): if isinstance(force, mm.HarmonicBondForce): force.addBond(0, parm[-1].idx, 1, 500) self.assertRaises(exceptions.OpenMMWarning, lambda: openmm.load_topology(parm.topology, system)) warnings.filterwarnings('ignore', category=exceptions.OpenMMWarning) top = openmm.load_topology(parm.topology, system) self.assertIn(top[-1], top[0].bond_partners) self.assertEqual(len(top.bonds), len(parm.bonds) + 1)
def test_load_topology_error(self): """ Test error handling in load_topology """ parm = load_file(get_fn('ash.parm7'), get_fn('ash.rst7')) parm2 = load_file(get_fn('solv2.parm7'), get_fn('solv2.rst7')) self.assertRaises(TypeError, lambda: openmm.load_topology(parm.topology, system=get_fn('integrator.xml')) ) self.assertRaises(TypeError, lambda: openmm.load_topology(parm2.topology, system=parm.createSystem()) ) system = parm.createSystem() system.addForce(mm.CustomTorsionForce('theta**2')) self.assertRaises(exceptions.OpenMMWarning, lambda: openmm.load_topology(parm.topology, system))
def test_load_topology_uncondensed_atom_types(self): """ Tests condense_atom_types arg """ ommparm = app.AmberPrmtopFile(get_fn('complex.prmtop')) parm = load_file(get_fn('complex.prmtop')) system = ommparm.createSystem(implicitSolvent=app.OBC1) structure_condensed = openmm.load_topology(ommparm.topology, system, condense_atom_types=True) structure_uncondensed = openmm.load_topology(ommparm.topology, system, condense_atom_types=False) num_unique_atom_types_condensed = len(set([a.atom_type for a in structure_condensed])) num_unique_atom_types_uncondensed = len(set([a.atom_type for a in structure_uncondensed])) assert num_unique_atom_types_uncondensed > num_unique_atom_types_condensed # This may change if this or a similar flag does a partial condensing, see PR #1087 assert num_unique_atom_types_uncondensed == len(structure_uncondensed.atoms)
def test_box_from_system(self): """ Tests loading box from System """ parm = load_file(get_fn('solv2.parm7'), get_fn('solv2.rst7')) system = parm.createSystem(nonbondedMethod=app.PME, nonbondedCutoff=8*u.angstroms) top = openmm.load_topology(parm.topology, system) np.testing.assert_allclose(parm.box, top.box)
def test_box_from_system(self): """ Tests loading box from System """ parm = load_file(get_fn('solv2.parm7'), get_fn('solv2.rst7')) system = parm.createSystem(nonbondedMethod=app.PME, nonbondedCutoff=8 * u.angstroms) top = openmm.load_topology(parm.topology, system) np.testing.assert_allclose(parm.box, top.box)
def test_load_topology(self): """ Tests loading an OpenMM Topology and System instance """ ommparm = app.AmberPrmtopFile(get_fn('complex.prmtop')) parm = load_file(get_fn('complex.prmtop')) system = ommparm.createSystem(implicitSolvent=app.OBC1) structure = openmm.load_topology(ommparm.topology, system) self.assertEqual(len(parm.atoms), len(structure.atoms)) self.assertEqual(len(parm.residues), len(structure.residues)) self.assertEqual(len(parm.bonds), len(structure.bonds))
def test_round_trip_energy(tmpdir, molecule, method, openff, antechamber): """ Make sure that no terms are missing when storing parameters from source by comparing energies. Note we relax the comparison to abs=2e-3 due to differences in nonbonded cutoffs, phase rounding and the ordering improper torsions are applied. """ if method == "openff": engine = openff else: engine = antechamber with tmpdir.as_cwd(): mol = Ligand.from_file(get_data(molecule)) # parametrise the system engine.run(mol) # this will make a serialised system in the folder so get the reference energy ref_system = XmlSerializer.deserializeSystem( open("serialised.xml").read()) parm_top = load_topology(mol.to_openmm_topology(), system=ref_system, xyz=mol.openmm_coordinates()) ref_energy = energy_decomposition_system(parm_top, ref_system, platform="Reference") # now we need to build the system from our stored parameters mol.write_parameters(file_name="test.xml") ff = app.ForceField("test.xml") qube_system = ff.createSystem(mol.to_openmm_topology()) with open("qube.xml", "w") as xml_out: xml_out.write(XmlSerializer.serialize(qube_system)) qube_struc = load_topology(mol.to_openmm_topology(), system=qube_system, xyz=mol.openmm_coordinates()) qube_energy = energy_decomposition_system(qube_struc, qube_system, platform="Reference") # compare the decomposed energies of the groups for force_group, energy in ref_energy: for qube_force, qube_e in qube_energy: if force_group == qube_force: assert energy == pytest.approx(qube_e, abs=2e-3)
def test_load_topology_eps(self): """ Tests loading an OpenMM Topology with Extra Points """ parm = load_file(get_fn("tip4p.parm7"), get_fn("tip4p.rst7")) struct = openmm.load_topology(parm.topology, xyz=get_fn("tip4p.rst7")) has_eps = False self.assertEqual(len(parm.atoms), len(struct.atoms)) for a1, a2 in zip(parm.atoms, struct.atoms): self.assertIs(type(a1), type(a2)) has_eps = isinstance(a1, ExtraPoint) or has_eps self.assertTrue(has_eps) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_equal(parm.box, struct.box) # Now try passing in coordinates struct = openmm.load_topology(parm.topology, xyz=parm.coordinates) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_allclose(parm.box, struct.box) # taken from Topology struct = openmm.load_topology(parm.topology, xyz=parm.coordinates) np.testing.assert_equal(parm.coordinates, struct.coordinates) struct = openmm.load_topology(parm.topology, xyz=parm.coordinates, box=[10, 10, 10, 90, 90, 90]) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_equal(struct.box, [10, 10, 10, 90, 90, 90])
def testLoadTopology(self): """ Tests loading an OpenMM Topology and System instance """ import warnings warnings.filterwarnings('error', category=exceptions.OpenMMWarning) ommparm = app.AmberPrmtopFile(get_fn('complex.prmtop')) parm = load_file(get_fn('complex.prmtop')) system = ommparm.createSystem(implicitSolvent=app.OBC1) structure = openmm.load_topology(ommparm.topology, system) self.assertEqual(len(parm.atoms), len(structure.atoms)) self.assertEqual(len(parm.residues), len(structure.residues)) self.assertEqual(len(parm.bonds), len(structure.bonds)) warnings.filterwarnings('always', category=exceptions.OpenMMWarning)
def test_load_topology_eps(self): """ Tests loading an OpenMM Topology with Extra Points """ parm = load_file(get_fn('tip4p.parm7'), get_fn('tip4p.rst7')) struct = openmm.load_topology(parm.topology, xyz=get_fn('tip4p.rst7')) has_eps = False self.assertEqual(len(parm.atoms), len(struct.atoms)) for a1, a2 in zip(parm.atoms, struct.atoms): self.assertIs(type(a1), type(a2)) has_eps = isinstance(a1, ExtraPoint) or has_eps self.assertTrue(has_eps) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_equal(parm.box, struct.box) # Now try passing in coordinates struct = openmm.load_topology(parm.topology, xyz=parm.coordinates) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_allclose(parm.box, struct.box) # taken from Topology struct = openmm.load_topology(parm.topology, xyz=parm.coordinates) np.testing.assert_equal(parm.coordinates, struct.coordinates) struct = openmm.load_topology(parm.topology, xyz=parm.coordinates, box=[10, 10, 10, 90, 90, 90]) np.testing.assert_equal(parm.coordinates, struct.coordinates) np.testing.assert_equal(struct.box, [10, 10, 10, 90, 90, 90])
def test_simple(self): """ Test OpenMM System/Topology -> Gromacs topology conversion """ parm = load_file(get_fn('ash.parm7'), get_fn('ash.rst7')) self.assertEqual(parm.combining_rule, 'lorentz') system = parm.createSystem() gromacs.GromacsTopologyFile.from_structure( openmm.load_topology(parm.topology, system) ).write(get_fn('ash_from_omm.top', written=True)) parm2 = gromacs.GromacsTopologyFile(get_fn('ash_from_omm.top', written=True)) system2 = parm2.createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con1.setPositions(parm.positions) con2.setPositions(parm.positions) self._check_energies(parm, con1, parm2, con2)
def test_simple(self): """ Test OpenMM System/Topology -> Amber prmtop conversion """ parm = load_file(get_fn('ash.parm7'), get_fn('ash.rst7')) self.assertEqual(parm.combining_rule, 'lorentz') system = parm.createSystem() amber.AmberParm.from_structure( openmm.load_topology(parm.topology, system)).write_parm( get_fn('ash_from_omm.parm7', written=True)) parm2 = load_file(get_fn('ash_from_omm.parm7', written=True)) system2 = parm2.createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con1.setPositions(parm.positions) con2.setPositions(parm.positions) self.check_energies(parm, con1, parm2, con2)
def test_round_trip(self): """ Test ParmEd -> OpenMM round trip with CHARMM gas phase """ parm = charmm_gas system = parm.createSystem(param22) self.assertEqual(parm.combining_rule, 'lorentz') system2 = openmm.load_topology(parm.topology, system).createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system2, mm.VerletIntegrator(0.001), CPU) con1.setPositions(charmm_gas_crds.positions) con2.setPositions(charmm_gas_crds.positions) energies = energy_decomposition(parm, con1) energies2 = energy_decomposition(parm, con2) self.assertAlmostEqual(energies['bond'], energies2['bond']) self.assertAlmostEqual(energies['angle'], energies2['angle']) self.assertAlmostEqual(energies['urey_bradley'], energies2['urey_bradley']) self.assertAlmostEqual(energies['dihedral'], energies2['dihedral']) self.assertAlmostEqual(energies['improper'], energies2['improper']) self.assertAlmostEqual(energies['cmap'], energies2['cmap']) self.assertRelativeEqual(energies['nonbonded'], energies2['nonbonded'])
def testRoundTrip(self): """ Test ParmEd -> OpenMM round trip with CHARMM gas phase """ parm = charmm_gas system = parm.createSystem(param22) self.assertEqual(parm.combining_rule, "lorentz") system2 = openmm.load_topology(parm.topology, system).createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system2, mm.VerletIntegrator(0.001), CPU) con1.setPositions(charmm_gas_crds.positions) con2.setPositions(charmm_gas_crds.positions) energies = decomposed_energy(con1, parm) energies2 = decomposed_energy(con2, parm) self.assertAlmostEqual(energies["bond"], energies2["bond"]) self.assertAlmostEqual(energies["angle"], energies2["angle"]) self.assertAlmostEqual(energies["urey"], energies2["urey"]) self.assertAlmostEqual(energies["dihedral"], energies2["dihedral"]) self.assertAlmostEqual(energies["improper"], energies2["improper"]) self.assertAlmostEqual(energies["cmap"], energies2["cmap"]) self.assertRelativeEqual(energies["nonbond"], energies2["nonbond"])
def testRoundTrip(self): """ Test ParmEd -> OpenMM round trip with CHARMM gas phase """ parm = charmm_gas system = parm.createSystem(param22) self.assertEqual(parm.combining_rule, 'lorentz') system2 = openmm.load_topology(parm.topology, system).createSystem() con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system2, mm.VerletIntegrator(0.001), CPU) con1.setPositions(charmm_gas_crds.positions) con2.setPositions(charmm_gas_crds.positions) energies = energy_decomposition(parm, con1) energies2 = energy_decomposition(parm, con2) self.assertAlmostEqual(energies['bond'], energies2['bond']) self.assertAlmostEqual(energies['angle'], energies2['angle']) self.assertAlmostEqual(energies['urey_bradley'], energies2['urey_bradley']) self.assertAlmostEqual(energies['dihedral'], energies2['dihedral']) self.assertAlmostEqual(energies['improper'], energies2['improper']) self.assertAlmostEqual(energies['cmap'], energies2['cmap']) self.assertRelativeEqual(energies['nonbonded'], energies2['nonbonded'])
def test_load_topology_use_atom_id_as_typename(self): """ Tests loading an OpenMM Topology and using Atom.id to name types """ ommparm = load_file(get_fn('ash.parm7'), get_fn('ash.rst7')) parm = load_file(get_fn('ash.parm7'), get_fn('ash.rst7')) system = ommparm.createSystem(implicitSolvent=app.OBC1) for pmd_atom, omm_atom in zip(parm.atoms, ommparm.topology.atoms()): omm_atom.id = pmd_atom.type structure = openmm.load_topology(ommparm.topology, system, xyz=parm.positions) self.assertEqual(len(parm.atoms), len(structure.atoms)) self.assertEqual([a.type for a in parm.atoms], [a.type for a in structure.atoms]) self.assertEqual(len(parm.residues), len(structure.residues)) self.assertEqual(len(parm.bonds), len(structure.bonds)) con1 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con2 = mm.Context(system, mm.VerletIntegrator(0.001), CPU) con1.setPositions(parm.positions) con2.setPositions(structure.positions) self.check_energies(parm, con1, structure, con2)
def bad_system(): return openmm.load_topology(top.topology, system).createSystem()
def main(): parser = argparse.ArgumentParser(description='Runs local minimization on \ the protein-ligand complex using OpenMM') parser.add_argument('-l', '--ligand', action='store', nargs=1, dest='ligand', help='The ligand .pdb file to generate \ parameters for') parser.add_argument('-x', '--xml_ligand', action='store', nargs=1, dest='xml', help='The xml file containing ligand \ parameters - use full path if not in directory where \ this script is being executed from') parser.add_argument('-p', '--protein', action='store', nargs=1, dest='protein', help='The protein complex .pdb file') parser.add_argument('-i', '--input_directory', action='store', nargs=1, dest='input', default=['./'], help='Directory where \ input pdb files are stored') parser.add_argument('-o', '--output_directory', action='store', nargs=1, dest='output', default=['./'], help='Directory where \ output log should be written') args = vars(parser.parse_args()) #Load in ligand file and parameters ligand_pdbfile = PDBFile(args['input'][0] + '/' + args['ligand'][0]) ligand_system = parmed.load_file(args['xml'][0]) ligand_structure = load_topology(ligand_pdbfile.topology, ligand_system, xyz=ligand_pdbfile.positions) #Load in protein complex file and force field receptor_pdbfile = PDBFile(args['input'][0] + '/' + args['protein'][0]) receptor_pdbfile_name = args['protein'][0].split('.pdb')[-2] omm_forcefield = ForceField('amber14-all.xml') receptor_system = omm_forcefield.createSystem(receptor_pdbfile.topology) receptor_structure = load_topology(receptor_pdbfile.topology, receptor_system, xyz=receptor_pdbfile.positions) #Generate ligand-protein complex complex_structure = receptor_structure + ligand_structure complex_system = (complex_structure.createSystem(nonbondedMethod=NoCutoff, nonbondedCutoff=9.0 * angstrom, constraints=HBonds, removeCMMotion=False)) #Set up simulation parameters integrator = LangevinIntegrator(300 * kelvin, 1 / picosecond, 0.002 * picoseconds) simulation = Simulation(complex_structure.topology, complex_system, integrator) simulation.context.setPositions(complex_structure.positions) #Run local minimization simulation.minimizeEnergy() positions = simulation.context.getState(getPositions=True).getPositions() #Save minimized complex structure PDBFile.writeFile( simulation.topology, positions, open(args['output'][0] + '/' + receptor_pdbfile_name + '_min.pdb', 'w'))
def compare_energies(system_name, pdb_filename, psf_filename, ffxml_filenames, toppar_filenames, box_vectors_filename=None, system_kwargs=None, tolerance=1e-5, units=u.kilojoules_per_mole, write_serialized_xml=False): """ Compare energies between (pdb, psf, toppar) loaded via ParmEd and (pdb, ffxml) loaded by OpenMM ForceField Parameters ---------- system_name : str Name of the test system pdb_filename : str Name of PDB file that should contain CRYST entry and PDB format compliant CONECT records for HETATM residues. psf_filename : str CHARMM PSF file ffxml_filenames : list of str List of OpenMM ffxml files toppar_filenames : list of CHARMM toppar filenames to load into CharmmParameterSet List of CHARMM toppar files box_vectors_filename : str, optional, default=None If specified, read box vectors from a file like step2.1_waterbox.prm system_kwargs : dict, optional, default=None Keyword arguments to pass to CharmmPsfFile.createSystem() and ForceField.CreateSystem() when constructing System objects for energy comparison tolerance : float, optional, default=1e-5 Relative energy discrepancy tolerance units : simtk.unit.Unit Unit to use for energy comparison write_serialized_xml : bool, optional, default=False If True, will write out serialized System XML files for OpenMM systems to aid debugging. """ is_periodic = False if (system_kwargs is not None) and ('nonbondedMethod' in system_kwargs) and ( system_kwargs['nonbondedMethod'] in [app.CutoffPeriodic, app.PME]): is_periodic = True # Load PDB file pdbfile = app.PDBFile(pdb_filename) # Read box vectors if box_vectors_filename: box_vectors = read_box_vectors(box_vectors_filename) pdbfile.topology.setPeriodicBoxVectors(box_vectors) else: box_vectors = pdbfile.topology.getPeriodicBoxVectors() # Load CHARMM system through OpenMM openmm_toppar = app.CharmmParameterSet(*toppar_filenames) openmm_psf = app.CharmmPsfFile(psf_filename) # Set box vectors if is_periodic: openmm_psf.setBox(box_vectors[0][0], box_vectors[1][1], box_vectors[2][2]) openmm_system = openmm_psf.createSystem(openmm_toppar, **system_kwargs) openmm_structure = openmm.load_topology(openmm_psf.topology, openmm_system, xyz=pdbfile.positions) #openmm_energies = openmm.energy_decomposition_system(openmm_structure, openmm_system, nrg=units) #print('OpenMM CHARMM loader energy components : %s' % str(openmm_energies)) openmm_total_energy = compute_potential(openmm_system, pdbfile.positions) / units # Load CHARMM system through ParmEd parmed_toppar = CharmmParameterSet(*toppar_filenames) parmed_structure = CharmmPsfFile(psf_filename) #structure.load_parameters(toppar) parmed_structure.positions = pdbfile.positions # Set box vectors if is_periodic: parmed_structure.box = (box_vectors[0][0] / u.angstroms, box_vectors[1][1] / u.angstroms, box_vectors[2][2] / u.angstroms, 90, 90, 90) parmed_system = parmed_structure.createSystem(parmed_toppar, **system_kwargs) #parmed_energies = openmm.energy_decomposition_system(parmed_structure, parmed_system, nrg=units) #print('ParmEd CHARMM loader energy components : %s' % str(parmed_energies)) parmed_total_energy = compute_potential(parmed_system, pdbfile.positions) / units # Delete H-H bonds from waters and retreive updated topology and positions modeller = app.Modeller(openmm_psf.topology, pdbfile.positions) hhbonds = [ b for b in modeller.topology.bonds() if b[0].element == app.element.hydrogen and b[1].element == app.element.hydrogen ] modeller.delete(hhbonds) ffxml_topology = modeller.topology # OpenMM system with ffxml ff = app.ForceField(*ffxml_filenames) ffxml_system = ff.createSystem(ffxml_topology, **system_kwargs) ffxml_structure = openmm.load_topology(ffxml_topology, ffxml_system, xyz=pdbfile.positions) #ffxml_energies = openmm.energy_decomposition_system(ffxml_structure, ffxml_system, nrg=units) #print('ffxml energy components : %s' % str(ffxml_energies)) ffxml_total_energy = compute_potential(ffxml_system, pdbfile.positions) / units write_serialized_xml = True # DEBUG if write_serialized_xml: print('Writing serialized XML files...') write_serialized_system(system_name + '.charmm.system.xml', openmm_system) write_serialized_system(system_name + '.parmed.system.xml', parmed_system) write_serialized_system(system_name + '.openmm.system.xml', ffxml_system) print('-' * 100) print('') print('OpenMM CHARMM reader total energy: %14.3f' % openmm_total_energy) print('ParmEd CHARMM reader total energy: %14.3f' % parmed_total_energy) print('OPENMM ffxml total energy: %14.3f' % ffxml_total_energy) print('TOTAL ERROR: %14.3f' % (ffxml_total_energy - openmm_total_energy)) print('') print('-' * 100) # TODO : Automate comparison return # calc rel energies and assert rel_energies = [] for i, j in zip(ffxml_energies, parmed_energies): if i[0] != j[0]: raise Exception('Mismatch in energy tuples naming.') if abs(i[1]) > NEARLYZERO: rel_energies.append((i[0], abs((i[1] - j[1]) / i[1]))) else: if abs(j[1]) > NEARLYZERO: raise AssertionError( 'One of the CHARMM %s energies (%s) for %s is zero, ' 'while the corresponding OpenMM energy is non-zero' % (system_name, i[0], ffxml)) rel_energies.append((i[0], 0)) dihedrals_done = False for i in rel_energies: if i[0] != 'PeriodicTorsionForce': if i[1] > tolerance: raise AssertionError( '%s energies (%s, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], tolerance, ffxml)) else: if not dihedrals_done: if i[1] > tolerance: raise AssertionError( '%s energies (%s, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], tolerance, ffxml)) dihedrals_done = True else: #impropers if i[1] > improper_tolerance: raise AssertionError( '%s energies (%s-impropers, %f) outside of allowed tolerance (%f) for %s' % (system_name, i[0], i[1], improper_tolerance, ffxml)) # logging if not no_log: charmm_energies_log = dict() omm_energies_log = dict() rel_energies_log = dict() charmm_energies_log['ffxml_name'] = ffxml charmm_energies_log['test_system'] = system_name charmm_energies_log['data_type'] = 'CHARMM' charmm_energies_log['units'] = units omm_energies_log['ffxml_name'] = ffxml omm_energies_log['test_system'] = system_name omm_energies_log['data_type'] = 'OpenMM' omm_energies_log['units'] = units rel_energies_log['ffxml_name'] = ffxml rel_energies_log['test_system'] = system_name rel_energies_log['data_type'] = 'abs((CHARMM-OpenMM)/CHARMM)' dihedrals_done = False for item in amber_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: charmm_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: charmm_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: charmm_energies_log[item[0]] = item[1] dihedrals_done = False for item in omm_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: omm_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: omm_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: omm_energies_log[item[0]] = item[1] dihedrals_done = False for item in rel_energies: if item[0] == 'PeriodicTorsionForce' and not dihedrals_done: rel_energies_log['PeriodicTorsionForce_dihedrals'] = item[1] dihedrals_done = True elif item[0] == 'PeriodicTorsionForce' and dihedrals_done: rel_energies_log['PeriodicTorsionForce_impropers'] = item[1] elif item[0] == 'CMMotionRemover': continue else: rel_energies_log[item[0]] = item[1] logger.log(charmm_energies_log) logger.log(omm_energies_log) logger.log(rel_energies_log)