def test_coverage_filter_allowed():
    """
    Make sure the coverage filter removes the correct molecules.
    """

    coverage_filter = workflow_components.CoverageFilter(allowed_ids={"b83"})

    mols = get_stereoisomers()

    # we have to remove duplicated records
    # remove duplicates from the set
    molecule_container = get_container(mols)
    result = coverage_filter.apply(molecule_container.molecules,
                                   processors=1,
                                   toolkit_registry=GLOBAL_TOOLKIT_REGISTRY)

    forcefield = ForceField("openff_unconstrained-1.0.0.offxml")
    # now see if any molecules do not have b83
    parameters_by_id = {}
    for molecule in result.molecules:
        labels = forcefield.label_molecules(molecule.to_topology())[0]
        covered_types = set([
            label.id for types in labels.values() for label in types.values()
        ])
        # now store the smiles under the ids
        for parameter in covered_types:
            parameters_by_id.setdefault(parameter,
                                        []).append(molecule.to_smiles())

    expected = parameters_by_id["b83"]
    for molecule in result.molecules:
        assert molecule.to_smiles() in expected
        assert "dihedrals" not in molecule.properties
Exemple #2
0
def _build_system(mol, constrained):
    if constrained:
        parsley = ForceField("openff-1.0.0.offxml")
    else:
        parsley = ForceField("openff_unconstrained-1.0.0.offxml")

    mol = Molecule.from_file(get_data_file_path("molecules/" + mol),
                             file_format="sdf")

    if type(mol) == Molecule:
        off_top = mol.to_topology()
        positions = mol.conformers[0]
    elif type(mol) == list:
        # methane_multiconformer case is a list of two mols
        off_top = Topology()
        for mol_i in mol:
            off_top.add_molecule(mol_i)
        positions = (np.vstack([mol[0].conformers[0], mol[1].conformers[0]]) *
                     unit.angstrom)

    from openff.toolkit.utils.toolkits import (
        AmberToolsToolkitWrapper,
        RDKitToolkitWrapper,
        ToolkitRegistry,
    )

    toolkit_registry = ToolkitRegistry(
        toolkit_precedence=[RDKitToolkitWrapper, AmberToolsToolkitWrapper])

    omm_sys = parsley.create_openmm_system(off_top,
                                           toolkit_registry=toolkit_registry)

    return omm_sys, positions, off_top
Exemple #3
0
    def get_parameter_handler_from_forcefield(self, parameter_handler_name,
                                              forcefield):
        """
        It returns a parameter handler from the forcefield based on its
        name.

        Parameters
        ----------
        parameter_handler_name : str
            The name of the parameter handler that is requested
        forcefield : an openforcefield.typing.engines.smirnoff.ForceField
                     object
            The forcefield from which the parameter handler will be obtained

        Returns
        -------
        parameter_handler : an openforcefield.typing.engines.smirnoff.parameters.ParameterHandler
                            object
            The ParameterHandler that was requested
        """
        from openff.toolkit.typing.engines.smirnoff import ForceField

        if isinstance(forcefield, str):
            forcefield = ForceField(forcefield)
        elif isinstance(forcefield, ForceField):
            pass
        else:
            raise Exception('Invalid forcefield type')

        return forcefield.get_parameter_handler(parameter_handler_name)
Exemple #4
0
def smirnoff_force_field() -> OFFForceField:
    from simtk import unit

    off_force_field = OFFForceField(
        '<SMIRNOFF version="0.3" aromaticity_model="OEAroModel_MDL"></SMIRNOFF>'
    )

    vdw_handler = vdWHandler(**{"version": "0.3"})
    vdw_handler.add_parameter(
        parameter_kwargs={
            "smirks": "[#6:1]",
            "epsilon": 1.0 * unit.kilojoules_per_mole,
            "sigma": 1.0 * unit.angstrom,
        })
    off_force_field.register_parameter_handler(vdw_handler)

    charge_handler = ChargeIncrementModelHandler(**{"version": "0.3"})
    charge_handler.add_parameter(
        parameter_kwargs={
            "smirks": "[#6:1]-[#6:2]",
            "charge_increment1": 1.0 * unit.elementary_charge,
            "charge_increment2": -1.0 * unit.elementary_charge,
        })
    off_force_field.register_parameter_handler(charge_handler)

    return off_force_field
Exemple #5
0
def test_force_field_custom_handler(mock_entry_point_plugins):
    """Tests a force field can make use of a custom parameter handler registered
    through the entrypoint plugin system.
    """

    # Construct a simple FF which only uses the custom handler.
    force_field_contents = "\n".join([
        "<?xml version='1.0' encoding='ASCII'?>",
        "<SMIRNOFF version='0.3' aromaticity_model='OEAroModel_MDL'>",
        "  <CustomHandler version='0.3'></CustomHandler>",
        "</SMIRNOFF>",
    ])

    # An exception should be raised when plugins aren't allowed.
    with pytest.raises(KeyError) as error_info:
        ForceField(force_field_contents)

    assert (
        "Cannot find a registered parameter handler class for tag 'CustomHandler'"
        in error_info.value.args[0])

    # Otherwise the FF should be created as expected.
    force_field = ForceField(force_field_contents, load_plugins=True)

    parameter_handler = force_field.get_parameter_handler("CustomHandler")
    assert parameter_handler is not None
    assert parameter_handler.__class__.__name__ == "CustomHandler"
Exemple #6
0
def _create_impropers_only_system(
    smiles: str = "CC1=C(C(=O)C2=C(C1=O)N3CC4C(C3(C2COC(=O)N)OC)N4)N",
) -> mm.System:
    """Create a simulation that contains only improper torsion terms,
    by parameterizing with openff-1.2.0 and deleting  all terms but impropers
    """

    molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True)
    g = esp.Graph(molecule)

    topology = Topology.from_molecules(molecule)
    forcefield = ForceField("openff-1.2.0.offxml")
    openmm_system = forcefield.create_openmm_system(topology)

    # delete all forces except PeriodicTorsionForce
    is_torsion = (
        lambda force: "PeriodicTorsionForce" in force.__class__.__name__
    )
    for i in range(openmm_system.getNumForces())[::-1]:
        if not is_torsion(openmm_system.getForce(i)):
            openmm_system.removeForce(i)
    assert openmm_system.getNumForces() == 1
    torsion_force = openmm_system.getForce(0)
    assert is_torsion(torsion_force)

    # set k = 0 for any torsion that's not an improper
    indices = set(
        map(
            tuple,
            esp.graphs.utils.offmol_indices.improper_torsion_indices(
                molecule
            ),
        )
    )
    num_impropers_retained = 0
    for i in range(torsion_force.getNumTorsions()):
        (
            p1,
            p2,
            p3,
            p4,
            periodicity,
            phase,
            k,
        ) = torsion_force.getTorsionParameters(i)

        if (p1, p2, p3, p4) in indices:
            num_impropers_retained += 1
        else:
            torsion_force.setTorsionParameters(
                i, p1, p2, p3, p4, periodicity, phase, 0.0
            )

    assert (
        num_impropers_retained > 0
    )  # otherwise this molecule is not a useful test case!

    return openmm_system, topology, g
Exemple #7
0
def test_combine_molecules_offxml_plugin_deepdiff(tmpdir, coumarin, rfree_data):
    """Make sure that systems made from molecules using the xml method match offxmls with plugins"""

    # we need to recalculate the Nonbonded terms using the fake Rfree
    coumarin_copy = coumarin.copy(deep=True)
    # apply symmetry to make sure systems match
    MBISCharges.apply_symmetrisation(coumarin_copy)
    with tmpdir.as_cwd():
        # make the offxml using the plugin interface
        _combine_molecules_offxml(
            molecules=[coumarin_copy],
            parameters=elements,
            rfree_data=rfree_data,
            filename="openff.offxml",
            water_model="tip3p",
        )
        offxml = ForceField(
            "openff.offxml", load_plugins=True, allow_cosmetic_attributes=True
        )
        # check the plugin is being used
        vdw = offxml.get_parameter_handler("QUBEKitvdWTS")
        # make sure we have the parameterize tags
        assert len(vdw._cosmetic_attribs) == 1
        assert len(vdw.parameters) == 28

        alpha = rfree_data.pop("alpha")
        beta = rfree_data.pop("beta")
        lj = LennardJones612(free_parameters=rfree_data, alpha=alpha, beta=beta)
        # get new Rfree data
        lj.run(coumarin_copy)
        coumarin_copy.write_parameters("coumarin.xml")
        coumarin_ref_system = xmltodict.parse(
            XmlSerializer.serialize(
                app.ForceField("coumarin.xml").createSystem(
                    topology=coumarin_copy.to_openmm_topology(),
                    nonbondedCutoff=9 * unit.angstroms,
                    removeCMMotion=False,
                )
            )
        )
        coumarin_off_system = xmltodict.parse(
            XmlSerializer.serialize(
                offxml.create_openmm_system(
                    Molecule.from_rdkit(coumarin_copy.to_rdkit()).to_topology()
                )
            )
        )

        coumarin_diff = DeepDiff(
            coumarin_ref_system,
            coumarin_off_system,
            ignore_order=True,
            significant_digits=6,
            exclude_regex_paths="mass",
        )
        assert len(coumarin_diff) == 1
        for item in coumarin_diff["iterable_item_added"].values():
            assert item["@k"] == "0"
def hydrogen_chloride_force_field(library_charge: bool,
                                  charge_increment: bool) -> ForceField:
    """Returns a SMIRNOFF force field which is able to parameterize hydrogen chloride."""

    # Create the FF
    force_field = ForceField()

    # Add a Vdw handler.
    vdw_handler = vdWHandler(version=0.3)
    vdw_handler.method = "cutoff"
    vdw_handler.cutoff = 6.0 * simtk_unit.angstrom
    vdw_handler.scale14 = 1.0
    vdw_handler.add_parameter({
        "smirks": "[#1:1]",
        "epsilon": 0.0 * simtk_unit.kilojoules_per_mole,
        "sigma": 1.0 * simtk_unit.angstrom,
    })
    vdw_handler.add_parameter({
        "smirks": "[#17:1]",
        "epsilon": 2.0 * simtk_unit.kilojoules_per_mole,
        "sigma": 2.0 * simtk_unit.angstrom,
    })
    force_field.register_parameter_handler(vdw_handler)

    # Add an electrostatic, a library charge and a charge increment handler.
    electrostatics_handler = ElectrostaticsHandler(version=0.3)
    electrostatics_handler.cutoff = 6.0 * simtk_unit.angstrom
    electrostatics_handler.method = "PME"
    force_field.register_parameter_handler(electrostatics_handler)

    if library_charge:

        library_charge_handler = LibraryChargeHandler(version=0.3)
        library_charge_handler.add_parameter(
            parameter_kwargs={
                "smirks": "[#1:1]",
                "charge1": 1.0 * simtk_unit.elementary_charge,
            })
        library_charge_handler.add_parameter(
            parameter_kwargs={
                "smirks": "[#17:1]",
                "charge1": -1.0 * simtk_unit.elementary_charge,
            })
        force_field.register_parameter_handler(library_charge_handler)

    if charge_increment:

        charge_increment_handler = ChargeIncrementModelHandler(version=0.3)
        charge_increment_handler.add_parameter(
            parameter_kwargs={
                "smirks": "[#1:1]-[#17:2]",
                "charge_increment1": -1.0 * simtk_unit.elementary_charge,
                "charge_increment2": 1.0 * simtk_unit.elementary_charge,
            })
        force_field.register_parameter_handler(charge_increment_handler)

    return force_field
Exemple #9
0
def test_combine_molecules_deepdiff_offxml(
    acetone, openff, coumarin, tmpdir, rfree_data
):
    """When not optimising anything make sure we can round trip openff parameters"""

    with tmpdir.as_cwd():
        openff.run(acetone)
        acetone_ref_system = xmltodict.parse(open("serialised.xml").read())
        openff.run(coumarin)
        coumarin_ref_system = xmltodict.parse(open("serialised.xml").read())

        _combine_molecules_offxml(
            molecules=[acetone, coumarin],
            parameters=[],
            rfree_data=rfree_data,
            filename="combined.offxml",
        )

        # load up new systems and compare
        combinded_ff = ForceField("combined.offxml")
        assert combinded_ff.author == f"QUBEKit_version_{qubekit.__version__}"

        acetone_combine_system = xmltodict.parse(
            XmlSerializer.serialize(
                combinded_ff.create_openmm_system(
                    topology=Molecule.from_rdkit(acetone.to_rdkit()).to_topology()
                )
            )
        )
        acetone_diff = DeepDiff(
            acetone_ref_system,
            acetone_combine_system,
            ignore_order=True,
            significant_digits=6,
        )
        # should only be a difference in torsions with zero k values as openff drops these
        assert len(acetone_diff) == 1
        for item in acetone_diff["iterable_item_added"].values():
            assert item["@k"] == "0"

        coumarin_combine_system = xmltodict.parse(
            XmlSerializer.serialize(
                combinded_ff.create_openmm_system(
                    topology=Molecule.from_rdkit(coumarin.to_rdkit()).to_topology()
                )
            )
        )
        coumarin_diff = DeepDiff(
            coumarin_ref_system,
            coumarin_combine_system,
            ignore_order=True,
            significant_digits=6,
        )
        assert len(coumarin_diff) == 1
        for item in coumarin_diff["iterable_item_added"].values():
            assert item["@k"] == "0"
Exemple #10
0
 def _get_forcefield(cls, **kwargs):
     if "ff_path" in kwargs:
         try:
             return ForceField(kwargs['ff_path'],
                               allow_cosmetic_attributes=True)
         except Exception as e:
             print(
                 "Specified forcefield cannot be found. Fallback to default forcefield"
             )
     return ForceField('test_forcefields/smirnoff99Frosst.offxml')
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)
Exemple #12
0
def test_scaled_de_energy():
    """For a molecule with 1-4 interactions make sure the scaling is correctly applied.
    Note that only nonbonded parameters are non zero.
    """

    ff = ForceField(load_plugins=True)
    ff.get_parameter_handler("Electrostatics")

    ff.get_parameter_handler(
        "ChargeIncrementModel",
        {
            "version": "0.3",
            "partial_charge_method": "formal_charge"
        },
    )
    vdw_handler = ff.get_parameter_handler("vdW")
    vdw_handler.add_parameter({
        "smirks": "[*:1]",
        "epsilon": 0.0 * unit.kilojoule_per_mole,
        "sigma": 1.0 * unit.angstrom,
    })
    double_exp = ff.get_parameter_handler("DoubleExponential")
    double_exp.alpha = 18.7
    double_exp.beta = 3.3
    double_exp.scale14 = 1
    double_exp.add_parameter({
        "smirks": "[#6X4:1]",
        "r_min": 3.816 * unit.angstrom,
        "epsilon": 0.1094 * unit.kilocalorie_per_mole,
    })
    double_exp.add_parameter({
        "smirks": "[#1:1]-[#6X4]",
        "r_min": 2.974 * unit.angstrom,
        "epsilon": 0.0157 * unit.kilocalorie_per_mole,
    })

    ethane = Molecule.from_smiles("CC")
    ethane.generate_conformers(n_conformers=1)
    off_top = ethane.to_topology()
    omm_top = off_top.to_openmm()
    system_no_scale = ff.create_openmm_system(topology=off_top)
    energy_no_scale = evaluate_energy(system=system_no_scale,
                                      topology=omm_top,
                                      positions=ethane.conformers[0])
    # now scale 1-4 by half
    double_exp.scale14 = 0.5
    system_scaled = ff.create_openmm_system(topology=off_top)
    energy_scaled = evaluate_energy(system=system_scaled,
                                    topology=omm_top,
                                    positions=ethane.conformers[0])
    assert double_exp.scale14 * energy_no_scale == pytest.approx(energy_scaled,
                                                                 abs=1e-6)
Exemple #13
0
def test_sort_smirnoff_dict():
    from collections import OrderedDict

    from openff.toolkit.typing.engines.smirnoff import ForceField
    from openff.toolkit.utils.utils import sort_smirnoff_dict

    forcefield = ForceField("test_forcefields/test_forcefield.offxml")
    smirnoff_dict = forcefield._to_smirnoff_data()

    # Ensure data is not created or destroyed
    # dict.__eq__ does not check order
    assert smirnoff_dict == OrderedDict(
        sort_smirnoff_dict(forcefield._to_smirnoff_data()))
def checkTorsion(molList, ff_name):
    """
    Take mollist and check if the molecules in a list match a specific torsion id

        Parameters
        ----------
        molList : List of objects
            List of oemols with datatags generated in genData function

        Returns
        -------
        molList : list of objects
            List of oemol objects that have a datatag "IDMatch" that contain the torsion id
            involved in the QCA torsion drive
    """

    matches = []
    count = 0
    mols = []
    for mol in molList:
        molecule = Molecule.from_mapped_smiles(mol.GetData("cmiles"))
        topology = Topology.from_molecules(molecule)
        # Let's label using the Parsley force field
        forcefield = ForceField(ff_name, allow_cosmetic_attributes=True)
        # Run the molecule labeling
        molecule_force_list = forcefield.label_molecules(topology)
        params = []
        # Print out a formatted description of the torsion parameters applied to this molecule
        for mol_idx, mol_forces in enumerate(molecule_force_list):
            # print(f'Forces for molecule {mol_idx}')
            for force_tag, force_dict in mol_forces.items():
                if force_tag == "ProperTorsions":
                    for (atom_indices, parameter) in force_dict.items():
                        params.append(parameter.id)
                        if atom_indices == mol.GetData("TDindices") or tuple(
                            reversed(atom_indices)
                        ) == mol.GetData("TDindices"):
                            count += 1
                            mol.SetData("IDMatch", parameter.id)
                            mols.append(mol)
    print(
        "Out of "
        + str(len(molList))
        + " molecules, "
        + str(count)
        + " were processed with checkTorsion()"
    )

    return mols
Exemple #15
0
def test_residues():
    pdb = app.PDBFile(get_test_file_path("ALA_GLY/ALA_GLY.pdb"))
    traj = md.load(get_test_file_path("ALA_GLY/ALA_GLY.pdb"))
    mol = Molecule(get_test_file_path("ALA_GLY/ALA_GLY.sdf"),
                   file_format="sdf")

    top = OFFBioTop.from_openmm(pdb.topology, unique_molecules=[mol])
    top.mdtop = traj.top

    assert top.n_topology_atoms == 29
    assert top.mdtop.n_residues == 4
    assert [r.name for r in top.mdtop.residues] == ["ACE", "ALA", "GLY", "NME"]

    ff = ForceField("openff-1.3.0.offxml")
    off_sys = Interchange.from_smirnoff(ff, top)

    # Assign positions and box vectors in order to run MM
    off_sys.positions = pdb.positions
    off_sys.box = [4.8, 4.8, 4.8]

    # Just ensure that a single-point energy can be obtained without error
    get_openmm_energies(off_sys)

    assert len(top.mdtop.select("resname ALA")) == 10
    assert [*off_sys.topology.mdtop.residues][-1].n_atoms == 6
Exemple #16
0
def test_amber_energy():
    """Basic test to see if the amber energy driver is functional"""
    mol = Molecule.from_smiles("CCO")
    mol.generate_conformers(n_conformers=1)
    top = mol.to_topology()
    top.mdtop = md.Topology.from_openmm(top.to_openmm())

    parsley = ForceField("openff_unconstrained-1.0.0.offxml")
    off_sys = Interchange.from_smirnoff(parsley, top)

    off_sys.box = [4, 4, 4]
    off_sys.positions = mol.conformers[0]

    omm_energies = get_gromacs_energies(off_sys, mdp="cutoff_hbonds")
    amb_energies = get_amber_energies(off_sys)

    omm_energies.compare(
        amb_energies,
        custom_tolerances={
            "Bond": 3.6 * kj_mol,
            "Angle": 0.2 * kj_mol,
            "Torsion": 1.9 * kj_mol,
            "vdW": 1.5 * kj_mol,
            "Electrostatics": 36.5 * kj_mol,
        },
    )
Exemple #17
0
def _load_force_field() -> Union["ForceField", "ForceFieldSource"]:
    """Load in the force field to use in the benchmark."""

    from openff.evaluator.forcefield import ForceFieldSource
    from openff.toolkit.typing.engines.smirnoff import ForceField

    if os.path.isfile("force-field.offxml") and os.path.isfile(
            "force-field.json"):

        raise RuntimeError(
            "Two valid force fields were found: force-field.offxml and "
            "force-field.json")

    elif os.path.isfile("force-field.offxml"):
        force_field = ForceField("force-field.offxml")
    elif os.path.isfile("force-field.json"):
        force_field = ForceFieldSource.from_json("force-field.json")
    else:
        raise RuntimeError(
            "No valid force field could be found. Either a SMIRNOFF force field "
            "(named force-field.offxml) or an OpenFF Evaluator force field source "
            "(named force-field.json) must be present in the current directory."
        )

    return force_field
def test_openmm_roundtrip():
    mol = Molecule.from_smiles("CCO")
    mol.generate_conformers(n_conformers=1)
    top = mol.to_topology()
    omm_top = top.to_openmm()

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

    off_sys = Interchange.from_smirnoff(parsley, top)

    off_sys.box = [4, 4, 4]
    off_sys.positions = mol.conformers[0].value_in_unit(simtk_unit.nanometer)

    omm_sys = off_sys.to_openmm()

    converted = from_openmm(
        topology=omm_top,
        system=omm_sys,
    )

    converted.topology = off_sys.topology
    converted.box = off_sys.box
    converted.positions = off_sys.positions

    get_openmm_energies(off_sys).compare(
        get_openmm_energies(converted),
        custom_tolerances={"Nonbonded": 1.5 * simtk_unit.kilojoule_per_mole},
    )
Exemple #19
0
def load_forcefield(forcefield="openff_unconstrained-1.2.0"):
    # get a forcefield
    try:
        ff = ForceField("%s.offxml" % forcefield)
    except:
        raise NotImplementedError
    return ff
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
Exemple #21
0
def test_torsion_smirnoff_coverage(public_client, monkeypatch):

    molecule: Molecule = Molecule.from_mapped_smiles(
        "[H:1][C:2]([H:7])([H:8])"
        "[C:3]([H:9])([H:10])"
        "[C:4]([H:11])([H:12])"
        "[C:5]([H:13])([H:14])[H:6]")

    dihedrals = [(0, 1, 2, 3), (1, 2, 3, 4), (2, 3, 4, 5)]

    collection = TorsionDriveResultCollection(
        entries={
            "http://localhost:442": [
                TorsionDriveResult(
                    record_id=ObjectId(str(i + 1)),
                    cmiles=molecule.to_smiles(mapped=True),
                    inchi_key=molecule.to_inchikey(),
                ) for i in range(len(dihedrals))
            ]
        })

    monkeypatch.setattr(
        TorsionDriveResultCollection,
        "to_records",
        lambda self: [(
            TorsionDriveRecord(
                id=entry.record_id,
                qc_spec=QCSpecification(
                    driver=DriverEnum.gradient,
                    method="scf",
                    basis="sto-3g",
                    program="psi4",
                ),
                optimization_spec=OptimizationSpecification(
                    program="geometric", keywords={}),
                initial_molecule=[ObjectId(1)],
                status=RecordStatusEnum.complete,
                client=public_client,
                keywords=TDKeywords(dihedrals=
                                    [dihedrals[int(entry.record_id) - 1]],
                                    grid_spacing=[]),
                final_energy_dict={},
                optimization_history={},
                minimum_positions={},
            ),
            molecule,
        ) for address, entries in self.entries.items() for entry in entries],
    )

    coverage = collection.smirnoff_coverage(ForceField("openff-1.3.0.offxml"),
                                            driven_only=True)

    assert {*coverage} == {"Bonds", "Angles", "ProperTorsions"}

    assert {*coverage["Bonds"].values()} == {3}
    assert {*coverage["Angles"].values()} == {3}

    assert {*coverage["ProperTorsions"].values()} == {1, 3}
Exemple #22
0
def test_smirnoff_hack():
    """Test basic behavior of smirnoff_hack.py, in particular
    the compatibility with Molecule API"""
    from openff.toolkit.topology import Molecule, Topology
    from openff.toolkit.typing.engines.smirnoff import ForceField
    from openff.toolkit.utils.toolkits import (AmberToolsToolkitWrapper,
                                               RDKitToolkitWrapper,
                                               ToolkitRegistry)

    from forcebalance import smirnoff_hack

    top = Topology.from_molecules(Molecule.from_smiles("CCO"))
    parsley = ForceField("openff-1.0.0.offxml")

    registry = ToolkitRegistry()
    registry.register_toolkit(RDKitToolkitWrapper)
    registry.register_toolkit(AmberToolsToolkitWrapper)
    parsley.create_openmm_system(top, toolkit_registry=registry)
Exemple #23
0
def test_get_water_fail():
    """Make sure an error is rasied if we requested a non-supported water model"""

    with pytest.raises(NotImplementedError):
        _add_water_model(
            ForceField("openff_unconstrained-2.0.0.offxml"),
            water_model="tip4p",
            using_plugin=True,
        )
def test_from_openmm_single_mols(mol, n_mols):
    """
    Test that ForceField.create_openmm_system and Interchange.to_openmm produce
    objects with similar energies

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

    parsley = ForceField(get_test_file_path("parsley.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) * simtk_unit.angstrom

    top.box_vectors = np.eye(3) * np.asarray([15, 15, 15]) * simtk_unit.nanometer

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

    toolkit_system = parsley.create_openmm_system(top)

    native_system = Interchange.from_smirnoff(
        force_field=parsley, topology=top
    ).to_openmm()

    toolkit_energy = _get_openmm_energies(
        omm_sys=toolkit_system,
        box_vectors=toolkit_system.getDefaultPeriodicBoxVectors(),
        positions=positions,
    )
    native_energy = _get_openmm_energies(
        omm_sys=native_system,
        box_vectors=native_system.getDefaultPeriodicBoxVectors(),
        positions=positions,
    )

    toolkit_energy.compare(native_energy)
    def test_prepare_force_field(self, optimization):
        """Test that the correct cosmetic attributes are attached to the FF, especially
        in the special case of BCC handlers."""

        optimization.parameters_to_train.append(
            Parameter(
                handler_type="ChargeIncrementModel",
                smirks="[#6:1]-[#6:2]",
                attribute_name="charge_increment1",
            ))
        optimization.parameters_to_train.append(
            Parameter(
                handler_type="vdW",
                smirks=None,
                attribute_name="scale14",
            ))

        with temporary_cd():

            OptimizationInputFactory._prepare_force_field(optimization, None)

            assert os.path.isfile(
                os.path.join("forcefield", "force-field.offxml"))

            off_force_field = OFFForceField(
                os.path.join("forcefield", "force-field.offxml"),
                allow_cosmetic_attributes=True,
            )

        vdw_handler = off_force_field.get_parameter_handler("vdW")
        assert vdw_handler._parameterize == "scale14"

        assert len(vdw_handler.parameters) == 1
        parameter = vdw_handler.parameters["[#6:1]"]
        assert parameter._parameterize == "epsilon, sigma"

        bcc_handler = off_force_field.get_parameter_handler(
            "ChargeIncrementModel")
        assert len(bcc_handler.parameters) == 1
        parameter = bcc_handler.parameters["[#6:1]-[#6:2]"]
        assert len(parameter.charge_increment) == 1
        assert parameter._parameterize == "charge_increment1"
def single_molecule_coverage(molecule: Molecule, forcefield: ForceField):
    #-> Dict[str, Dict[str, int]], List[Molecule], List[Molecule, Exception]
    """
    For a single molecule generate a coverage report and try to build an openmm system this will also highlight any missing parameters and dificulties with charging the molecule.

    Parameters
    ----------
    molecule: The openff-toolkit molecule object for which the report should be generated.
    ff: The openff-toolkit typing engine that should be used to check coverage and build an openmm system.

    Returns
    -------
    report: dict
        A dictionary of the coverage report 
    e: Exception or None. 
        The exception raised in this step, if any. 
        If not None, it should be assumed that coverage is invalid.
    """

    coverage = {
        "Angles": {},
        "Bonds": {},
        "ProperTorsions": {},
        "ImproperTorsions": {},
        "vdW": {}
    }
    coverage["molecule"] = molecule
    try:
        labels = forcefield.label_molecules(molecule.to_topology())[0]
        for param_type, params in labels.items():
            for param in params.values():
                if param.id not in coverage[param_type]:
                    coverage[param_type][param.id] = 1
                else:
                    coverage[param_type][param.id] += 1
        # now generate a system this will catch any missing parameters
        # and molecules that can not be charged
        _ = forcefield.create_openmm_system(molecule.to_topology())
        return coverage, None
    except Exception as e:
        return coverage, e
def test_to_lammps_single_mols(mol, n_mols):
    """
    Test that Interchange.to_openmm Interchange.to_lammps report sufficiently 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 = OFFBioTop.from_molecules(n_mols * [mol])
    top.mdtop = md.Topology.from_openmm(top.to_openmm())
    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 = Interchange.from_smirnoff(parsley, 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,
    )

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

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

    lmp_energies.compare(
        reference,
        custom_tolerances={
            "Nonbonded": 100 * omm_unit.kilojoule_per_mole,
            "Electrostatics": 100 * omm_unit.kilojoule_per_mole,
            "vdW": 100 * omm_unit.kilojoule_per_mole,
            "Torsion": 3e-5 * omm_unit.kilojoule_per_mole,
        },
    )
def ideal_water_force_field() -> ForceField:
    """Returns a force field that will assign constraints, a vdW handler and
    a library charge handler to a three site water molecule with all LJ
    ``epsilon=0.0`` and all ``q=0.0``.
    """
    ff = ForceField(load_plugins=True)

    constraint_handler = ff.get_parameter_handler("Constraints")
    constraint_handler.add_parameter({
        "smirks": "[#1:1]-[#8X2H2+0:2]-[#1]",
        "distance": 0.9572 * unit.angstrom
    })
    constraint_handler.add_parameter({
        "smirks": "[#1:1]-[#8X2H2+0]-[#1:2]",
        "distance": 1.5139 * unit.angstrom
    })
    # add a dummy vdW term
    vdw_handler = ff.get_parameter_handler("vdW")
    vdw_handler.add_parameter({
        "smirks": "[#1:1]-[#8X2H2+0]-[#1]",
        "epsilon": 0.0 * unit.kilojoule_per_mole,
        "sigma": 1.0 * unit.angstrom,
    })
    vdw_handler.add_parameter({
        "smirks": "[#1]-[#8X2H2+0:1]-[#1]",
        "epsilon": 0.0 * unit.kilojoules_per_mole,
        "sigma": 0.0 * unit.nanometers,
    })
    # add the library charges
    library_charge = ff.get_parameter_handler("LibraryCharges")
    library_charge.add_parameter({
        "smirks": "[#1]-[#8X2H2+0:1]-[#1]",
        "charge1": 0 * unit.elementary_charge
    })
    library_charge.add_parameter({
        "smirks": "[#1:1]-[#8X2H2+0]-[#1]",
        "charge1": 0 * unit.elementary_charge
    })

    return ff
Exemple #29
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 = OFFBioTop.from_openmm(
            pdbfile.topology,
            unique_molecules=unique_molecules,
        )
        top.mdtop = md.Topology.from_openmm(top.to_openmm())
        box = pdbfile.topology.getPeriodicBoxVectors()
        box = box.value_in_unit(nm) * unit.nanometer

        out = Interchange.from_smirnoff(ff, 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,
            combine_nonbonded_forces=True,
        ).compare(
            _get_openmm_energies(
                omm_sys=ff.create_openmm_system(top),
                box_vectors=pdbfile.topology.getPeriodicBoxVectors(),
                positions=pdbfile.getPositions(),
                hard_cutoff=True,
            )
        )
def test_unsupported_mixing_rule():
    molecules = [create_ethanol()]
    pdbfile = app.PDBFile(get_data_file_path("systems/test_systems/1_ethanol.pdb"))
    topology = OFFBioTop.from_openmm(pdbfile.topology, unique_molecules=molecules)
    topology.mdtop = md.Topology.from_openmm(topology.to_openmm())

    forcefield = ForceField("test_forcefields/test_forcefield.offxml")
    openff_sys = Interchange.from_smirnoff(force_field=forcefield, topology=topology)

    openff_sys["vdW"].mixing_rule = "geometric"

    with pytest.raises(UnsupportedExportError, match="default NonbondedForce"):
        openff_sys.to_openmm(combine_nonbonded_forces=True)