Пример #1
0
    def test_from_toolkit_packmol_boxes(self, pdb_path, unique_molecules):
        """
        Test loading some pre-prepared PACKMOL-generated systems.

        These use PDB files already prepared in the toolkit because PDB files are a pain.
        """
        ff = ForceField("openff-1.0.0.offxml")

        pdb_file_path = get_data_file_path("systems/packmol_boxes/" + pdb_path)
        pdbfile = openmm.app.PDBFile(pdb_file_path)
        top = Topology.from_openmm(
            pdbfile.topology,
            unique_molecules=unique_molecules,
        )
        box = pdbfile.topology.getPeriodicBoxVectors()
        box = box.value_in_unit(nm) * unit.nanometer

        out = ff.create_openff_system(top)
        out.box = box
        out.positions = pdbfile.getPositions()

        assert np.allclose(
            out.positions.to(unit.nanometer).magnitude,
            pdbfile.getPositions().value_in_unit(nm),
        )

        get_openmm_energies(out, hard_cutoff=True).compare(
            _get_openmm_energies(
                omm_sys=ff.create_openmm_system(top),
                box_vectors=pdbfile.topology.getPeriodicBoxVectors(),
                positions=pdbfile.getPositions(),
                hard_cutoff=True,
            ))
Пример #2
0
    def test_from_openmm_pdbfile(self, argon_ff, argon_top):
        pdb_file_path = get_test_file_path("10-argons.pdb")
        pdbfile = openmm.app.PDBFile(pdb_file_path)

        mol = Molecule.from_smiles("[#18]")
        top = Topology.from_openmm(pdbfile.topology, unique_molecules=[mol])
        box = pdbfile.topology.getPeriodicBoxVectors()
        box = box.value_in_unit(nm) * unit.nanometer

        out = argon_ff.create_openff_system(top)
        out.box = box
        out.positions = pdbfile.getPositions()

        assert np.allclose(
            out.positions.to(unit.nanometer).magnitude,
            pdbfile.getPositions().value_in_unit(nm),
        )

        get_openmm_energies(out, hard_cutoff=True).compare(
            _get_openmm_energies(
                omm_sys=argon_ff.create_openmm_system(top),
                box_vectors=pdbfile.topology.getPeriodicBoxVectors(),
                positions=pdbfile.getPositions(),
                hard_cutoff=True,
            ))
Пример #3
0
def test_to_lammps_single_mols(mol, n_mols):
    """
    Test that ForceField.create_openmm_system and System.to_openmm produce
    objects with similar energies

    TODO: Tighten tolerances
    TODO: Test periodic and non-periodic
    """

    parsley = ForceField("openff_unconstrained-1.0.0.offxml")

    mol = Molecule.from_smiles(mol)
    mol.generate_conformers(n_conformers=1)
    top = Topology.from_molecules(n_mols * [mol])
    mol.conformers[0] -= np.min(mol.conformers) * omm_unit.angstrom

    top.box_vectors = np.eye(3) * np.asarray([10, 10, 10]) * omm_unit.nanometer

    if n_mols == 1:
        positions = mol.conformers[0]
    elif n_mols == 2:
        positions = np.vstack(
            [mol.conformers[0], mol.conformers[0] + 3 * omm_unit.nanometer]
        )
        positions = positions * omm_unit.angstrom

    openff_sys = parsley.create_openff_system(topology=top)
    openff_sys.positions = positions.value_in_unit(omm_unit.nanometer)
    openff_sys.box = top.box_vectors

    reference = get_openmm_energies(
        off_sys=openff_sys, round_positions=3, electrostatics=False
    )

    lmp_energies = get_lammps_energies(
        off_sys=openff_sys, round_positions=3, electrostatics=False
    )

    _write_lammps_input(
        off_sys=openff_sys,
        file_name="tmp.in",
        electrostatics=False,
    )

    lmp_energies.compare(
        reference,
        custom_tolerances={
            "Nonbonded": 999 * omm_unit.kilojoule_per_mole,
            "Torsion": 0.005 * omm_unit.kilojoule_per_mole,
        },
    )
Пример #4
0
def test_argon_buck():
    mol = Molecule.from_smiles("[#18]")
    top = Topology.from_molecules([mol, mol])

    A = 1.69e-8 * unit.Unit("erg / mol") * Avogadro
    B = 1 / (0.273 * unit.angstrom)
    C = 102e-12 * unit.Unit("erg / mol") * unit.angstrom**6 * Avogadro

    A = A.to(unit.Unit("kilojoule/mol"))
    B = B.to(unit.Unit("1 / nanometer"))
    C = C.to(unit.Unit("kilojoule / mol * nanometer ** 6"))

    r = 0.3 * unit.nanometer

    buck = BuckinghamvdWHandler()
    coul = ElectrostaticsMetaHandler()  # Just to pass compatibility checks

    pot_key = PotentialKey(id="[#18]")
    pot = Potential(parameters={"A": A, "B": B, "C": C})

    for top_atom in top.topology_atoms:
        top_key = TopologyKey(atom_indices=(top_atom.topology_atom_index, ))
        buck.slot_map.update({top_key: pot_key})
        coul.charges.update({top_key: 0 * unit.elementary_charge})

    buck.potentials[pot_key] = pot

    from openff.system.components.system import System

    out = System()
    out.handlers["Buckingham-6"] = buck
    out.handlers["Electrostatics"] = coul
    out.topology = top
    out.box = [10, 10, 10] * unit.nanometer
    out.positions = [[0, 0, 0], [0.3, 0, 0]] * unit.nanometer
    out.to_gro("out.gro", writer="internal")
    out.to_top("out.top", writer="internal")

    gmx_energies = get_gromacs_energies(out, mdp="cutoff_buck")
    omm_energies = get_openmm_energies(out)
    by_hand = A * exp(-B * r) - C * r**-6

    gmx_energies.compare(omm_energies)

    resid = simtk_to_pint(gmx_energies.energies["Nonbonded"]) - by_hand
    assert resid < 1e-5 * unit.kilojoule / unit.mol
Пример #5
0
def test_argon(n_mol):
    from openff.system.utils import get_test_file_path

    ar_ff = ForceField(get_test_file_path("argon.offxml"))

    mol = Molecule.from_smiles("[#18]")
    mol.add_conformer(np.array([[0, 0, 0]]) * omm_unit.angstrom)
    mol.name = "FOO"
    top = Topology.from_molecules(n_mol * [mol])

    off_sys = ar_ff.create_openff_system(top)

    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=[n_mol],
        box=mb.Box([4, 4, 4]),
    )

    positions = packed_box.xyz * unit.nanometer
    positions = np.round(positions, 3)
    off_sys.positions = positions

    box = np.asarray(packed_box.box.lengths) * unit.nanometer
    off_sys.box = box

    omm_energies = get_openmm_energies(
        off_sys, round_positions=8, hard_cutoff=True, electrostatics=False
    )
    gmx_energies = get_gromacs_energies(
        off_sys, writer="internal", electrostatics=False
    )
    lmp_energies = get_lammps_energies(off_sys)

    omm_energies.compare(lmp_energies)

    omm_energies.compare(
        gmx_energies,
        custom_tolerances={
            "Nonbonded": 2e-5 * omm_unit.kilojoule_per_mole,
        },
    )
Пример #6
0
def test_water_dimer():
    from openff.system.utils import get_test_file_path

    tip3p = ForceField(get_test_file_path("tip3p.offxml"))
    water = Molecule.from_smiles("O")
    top = Topology.from_molecules(2 * [water])

    pdbfile = openmm.app.PDBFile(get_test_file_path("water-dimer.pdb"))

    positions = np.array(pdbfile.positions / omm_unit.nanometer) * unit.nanometer

    openff_sys = tip3p.create_openff_system(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)

    lmp_energies = get_lammps_energies(openff_sys, electrostatics=False)

    lmp_energies.compare(omm_energies)
Пример #7
0
def test_energies_single_mol(constrained, n_mol, mol_smi):
    mol = Molecule.from_smiles(mol_smi)
    mol.generate_conformers(n_conformers=1)
    mol.name = "FOO"
    top = Topology.from_molecules(n_mol * [mol])

    if constrained:
        parsley = ForceField("openff-1.0.0.offxml")
    else:
        parsley = ForceField("openff_unconstrained-1.0.0.offxml")

    off_sys = parsley.create_openff_system(top)

    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=[n_mol],
        density=500,  # kg/m^3
    )

    positions = packed_box.xyz * unit.nanometer
    off_sys.positions = positions

    box = np.asarray(packed_box.box.lengths) * unit.nanometer
    if np.any(box < 4 * unit.nanometer):
        off_sys.box = np.array([4, 4, 4]) * unit.nanometer
    else:
        off_sys.box = box

    # Compare directly to toolkit's reference implementation
    omm_energies = get_openmm_energies(
        off_sys, round_positions=8, hard_cutoff=True, electrostatics=False
    )
    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,
        hard_cutoff=True,
        electrostatics=False,
    )

    try:
        omm_energies.compare(reference_energies)
    except EnergyError as e:
        if "Nonbonded" in str(e):
            # If nonbonded energies differ, at least ensure that the nonbonded
            # parameters on each particle match
            from openff.system.tests.utils import (
                _get_charges_from_openmm_system,
                _get_lj_params_from_openmm_system,
            )
        else:
            raise e

        omm_sys = off_sys.to_openmm()
        np.testing.assert_equal(
            np.asarray([*_get_charges_from_openmm_system(omm_sys)]),
            np.asarray([*_get_charges_from_openmm_system(omm_reference)]),
        )
        np.testing.assert_equal(
            np.asarray([*_get_lj_params_from_openmm_system(omm_sys)]),
            np.asarray([*_get_lj_params_from_openmm_system(omm_reference)]),
        )

    mdp = "cutoff_hbonds" if constrained else "cutoff"
    # Compare GROMACS writer and OpenMM export
    gmx_energies = get_gromacs_energies(off_sys, mdp=mdp, electrostatics=False)

    custom_tolerances = {
        "Bond": 2e-5 * n_mol * omm_unit.kilojoule_per_mole,
        "Nonbonded": 1e-3 * n_mol * omm_unit.kilojoule_per_mole,
    }
    if constrained:
        # GROMACS might use the initial bond lengths, not the equilibrium bond lengths,
        # in the initial configuration, making angles differ slightly
        custom_tolerances.update(
            {
                "Angle": 5e-2 * n_mol * omm_unit.kilojoule_per_mole,
                "Nonbonded": 2.0 * n_mol * omm_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 = {
            "Nonbonded": 0.5 * n_mol * omm_unit.kilojoule_per_mole,
        }
        lmp_energies.compare(other_energies, custom_tolerances=custom_tolerances)
Пример #8
0
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 = Topology.from_openmm(
        omm_topology, unique_molecules=[ethanol, cyclohexane]
    )

    parsley = ForceField("openff_unconstrained-1.0.0.offxml")

    off_sys = parsley.create_openff_system(off_topology)

    off_sys.box = np.asarray(
        pdbfile.topology.getPeriodicBoxVectors() / omm_unit.nanometer,
    )
    off_sys.positions = np.asarray(
        pdbfile.positions / omm_unit.nanometer,
    )

    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={
            "Nonbonded": 2e-2 * omm_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 * omm_unit.kilojoule_per_mole,
            "Torsion": 1e-2 * omm_unit.kilojoule_per_mole,
            "Nonbonded": 3200 * omm_unit.kilojoule_per_mole,
        },
    )
Пример #9
0
def test_ethanol_opls():
    mol = Molecule.from_smiles("CC")
    mol.generate_conformers(n_conformers=1)
    top = mol.to_topology()
    parsley = ForceField("openff-1.0.0.offxml")
    out = parsley.create_openff_system(top)
    out.box = [4, 4, 4]
    out.positions = mol.conformers[0]
    out.positions = np.round(out.positions, 2)

    rb_torsions = RBTorsionHandler()
    smirks = "[#1:1]-[#6X4:2]-[#6X4:3]-[#1:4]"
    pot_key = PotentialKey(id=smirks)
    for proper in top.propers:
        top_key = TopologyKey(atom_indices=tuple(a.topology_atom_index
                                                 for a in proper))
        rb_torsions.slot_map.update({top_key: pot_key})

    # Values from HC-CT-CT-HC RB torsion
    # https://github.com/mosdef-hub/foyer/blob/7816bf53a127502520a18d76c81510f96adfdbed/foyer/forcefields/xml/oplsaa.xml#L2585
    pot = Potential(
        parameters={
            "c0": 0.6276 * kj_mol,
            "c1": 1.8828 * kj_mol,
            "c2": 0.0 * kj_mol,
            "c3": -2.5104 * kj_mol,
            "c4": 0.0 * kj_mol,
            "c5": 0.0 * kj_mol,
        })

    rb_torsions.potentials.update({pot_key: pot})

    out.handlers.update({"RBTorsions": rb_torsions})
    out.handlers.pop("ProperTorsions")

    gmx = get_openmm_energies(out, round_positions=3).energies["Torsion"]
    omm = get_gromacs_energies(out).energies["Torsion"]

    assert (gmx - omm).value_in_unit(omm_unit.kilojoule_per_mole) < 1e-3

    # Given that these force constants are copied from Foyer's OPLS-AA file,
    # compare to processing through the current MoSDeF pipeline
    try:
        import foyer
        import mbuild
    except ModuleNotFoundError:
        return

    comp = mbuild.load("CC", smiles=True)
    comp.xyz = mol.conformers[0].value_in_unit(omm_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"]

    assert (omm - rb_torsion_energy_from_foyer).value_in_unit(
        omm_unit.kilojoule_per_mole) < 1e-3