def test_generateResidueTemplate():
    """
    Test GAFF residue template generation from OEMol molecules.
    """
    from openeye import oechem, oeiupac

    from pkg_resources import resource_filename

    gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml")

    # Test independent ForceField instances.
    for molecule_name in IUPAC_molecule_names:
        mol = createOEMolFromIUPAC(molecule_name)
        # Generate an ffxml residue template.
        from openmoltools.forcefield_generators import generateResidueTemplate

        [template, ffxml] = generateResidueTemplate(mol)
        # Create a ForceField object.
        forcefield = ForceField(gaff_xml_filename)
        # Add the additional parameters and template to the forcefield.
        forcefield.registerResidueTemplate(template)
        forcefield.loadFile(StringIO(ffxml))
        # Create a Topology from the molecule.
        from openmoltools.forcefield_generators import generateTopologyFromOEMol

        topology = generateTopologyFromOEMol(mol)
        # Parameterize system.
        system = forcefield.createSystem(topology, nonbondedMethod=NoCutoff)
        # Check potential is finite.
        positions = extractPositionsFromOEMOL(mol)
        check_potential_is_finite(system, positions)

    # Test adding multiple molecules to a single ForceField instance.
    forcefield = ForceField(gaff_xml_filename)
    for molecule_name in IUPAC_molecule_names:
        mol = createOEMolFromIUPAC(molecule_name)
        # Generate an ffxml residue template.
        from openmoltools.forcefield_generators import generateResidueTemplate

        [template, ffxml] = generateResidueTemplate(mol)
        # Add the additional parameters and template to the forcefield.
        forcefield.registerResidueTemplate(template)
        forcefield.loadFile(StringIO(ffxml))
        # Create a Topology from the molecule.
        from openmoltools.forcefield_generators import generateTopologyFromOEMol

        topology = generateTopologyFromOEMol(mol)
        # Parameterize system.
        system = forcefield.createSystem(topology, nonbondedMethod=NoCutoff)
        # Check potential is finite.
        positions = extractPositionsFromOEMOL(mol)
        check_potential_is_finite(system, positions)
Exemplo n.º 2
0
def canonicalize_SMILES(smiles_list):
    """Ensure all SMILES strings end up in canonical form.
    Stereochemistry must already have been expanded.
    SMILES strings are converted to a OpenEye Topology and back again.
    Parameters
    ----------
    smiles_list : list of str
        List of SMILES strings
    Returns
    -------
    canonical_smiles_list : list of str
        List of SMILES strings, after canonicalization.
    """

    # Round-trip each molecule to a Topology to end up in canonical form
    from openmoltools.forcefield_generators import generateOEMolFromTopologyResidue, generateTopologyFromOEMol
    from openeye import oechem
    canonical_smiles_list = list()
    for smiles in smiles_list:
        molecule = smiles_to_oemol(smiles)
        topology = generateTopologyFromOEMol(molecule)
        residues = [ residue for residue in topology.residues() ]
        new_molecule = generateOEMolFromTopologyResidue(residues[0])
        new_smiles = oechem.OECreateIsoSmiString(new_molecule)
        canonical_smiles_list.append(new_smiles)
    return canonical_smiles_list
Exemplo n.º 3
0
def smiles_to_topology(smiles):
    """
    Convert a SMILES string to an OpenMM
    Topology

    Parameters
    ----------
    smiles : str
        smiles to be made into topology

    Returns
    -------
    topology : simtk.openmm.topology.app
        topology of the smiles
    mol : OEMol
        OEMol with explicit hydrogens
    """
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    mol = oechem.OEMol()
    oechem.OESmilesToMol(mol, smiles)
    oechem.OEAddExplicitHydrogens(mol)
    oechem.OETriposAtomNames(mol)
    oechem.OETriposBondTypeNames(mol)
    topology = generateTopologyFromOEMol(mol)
    return topology, mol
def test_generate_ffxml_from_molecules():
    """
    Test generation of single ffxml file from a list of molecules
    """
    # Create a test set of molecules.
    molecules = [createOEMolFromIUPAC(name) for name in IUPAC_molecule_names]
    # Create an ffxml file.
    from openmoltools.forcefield_generators import generateForceFieldFromMolecules

    ffxml = generateForceFieldFromMolecules(molecules)
    # Create a ForceField.
    gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml")
    forcefield = ForceField(gaff_xml_filename)
    try:
        forcefield.loadFile(StringIO(ffxml))
    except Exception as e:
        msg = str(e)
        msg += "ffxml contents:\n"
        for (index, line) in enumerate(ffxml.split("\n")):
            msg += "line %8d : %s\n" % (index, line)
        raise Exception(msg)

    # Parameterize the molecules.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol

    for molecule in molecules:
        # Create topology from molecule.
        topology = generateTopologyFromOEMol(molecule)
        # Create system with forcefield.
        system = forcefield.createSystem(topology)
        # Check potential is finite.
        positions = extractPositionsFromOEMOL(molecule)
        check_potential_is_finite(system, positions)
    def test_generate_Topology_and_OEMol(self):
        """
        Test round-trip from OEMol >> Topology >> OEMol
        """
        from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
        from openeye import oechem, oeiupac

        for molecule_name in IUPAC_molecule_names:
            molecule1 = createOEMolFromIUPAC(molecule_name)

            # Generate Topology from OEMol
            topology = generateTopologyFromOEMol(molecule1)
            # Check resulting Topology.
            residues = [residue for residue in topology.residues()]
            self.assertEqual(len(residues), 1)
            self.assertEqual(residues[0].name, molecule1.GetTitle())
            for (top_atom, mol_atom) in zip(topology.atoms(), molecule1.GetAtoms()):
                self.assertEqual(top_atom.name, mol_atom.GetName())
            for (top_bond, mol_bond) in zip(topology.bonds(), molecule1.GetBonds()):
                self.assertEqual(top_bond[0].name, mol_bond.GetBgn().GetName())
                self.assertEqual(top_bond[1].name, mol_bond.GetEnd().GetName())

            # Generate OEMol from Topology
            molecule2 = generateOEMolFromTopologyResidue(residues[0])
            # Check resulting molecule.
            self.assertEqual(molecule1.GetTitle(), molecule2.GetTitle())
            for (atom1, atom2) in zip(molecule1.GetAtoms(), molecule2.GetAtoms()):
                self.assertEqual(atom1.GetName(), atom2.GetName())
                self.assertEqual(atom1.GetAtomicNum(), atom2.GetAtomicNum())
            for (bond1, bond2) in zip(molecule1.GetBonds(), molecule2.GetBonds()):
                self.assertEqual(bond1.GetBgn().GetName(), bond2.GetBgn().GetName())
                self.assertEqual(bond1.GetEnd().GetName(), bond2.GetEnd().GetName())
Exemplo n.º 6
0
def oemol_to_openmm_system(oemol, molecule_name=None, forcefield=['data/gaff.xml']):
    from perses.rjmc import topology_proposal
    from openmoltools import forcefield_generators
    xml_filenames = [get_data_filename(fname) for fname in forcefield]
    system_generator = topology_proposal.SystemGenerator(xml_filenames, forcefield_kwargs={'constraints' : None})
    topology = forcefield_generators.generateTopologyFromOEMol(oemol)
    system = system_generator.build_system(topology)
    positions = extractPositionsFromOEMOL(oemol)
    return system, positions, topology
def oemol_to_omm_ff(oemol, molecule_name):
    from perses.rjmc import topology_proposal
    from openmoltools import forcefield_generators
    gaff_xml_filename = get_data_filename('data/gaff.xml')
    system_generator = topology_proposal.SystemGenerator([gaff_xml_filename])
    topology = forcefield_generators.generateTopologyFromOEMol(oemol)
    system = system_generator.build_system(topology)
    positions = extractPositionsFromOEMOL(oemol)
    return system, positions, topology
def oemol_to_openmm_system(oemol, molecule_name=None, forcefield=['data/gaff.xml']):
    from perses.rjmc import topology_proposal
    from openmoltools import forcefield_generators
    xml_filenames = [get_data_filename(fname) for fname in forcefield]
    system_generator = topology_proposal.SystemGenerator(xml_filenames, forcefield_kwargs={'constraints' : None})
    topology = forcefield_generators.generateTopologyFromOEMol(oemol)
    system = system_generator.build_system(topology)
    positions = extractPositionsFromOEMOL(oemol)
    return system, positions, topology
Exemplo n.º 9
0
def oemol_to_omm_ff(oemol, molecule_name):
    from perses.rjmc import topology_proposal
    from openmoltools import forcefield_generators
    gaff_xml_filename = get_data_filename('data/gaff.xml')
    system_generator = topology_proposal.SystemGenerator([gaff_xml_filename])
    topology = forcefield_generators.generateTopologyFromOEMol(oemol)
    system = system_generator.build_system(topology)
    positions = extractPositionsFromOEMOL(oemol)
    return system, positions, topology
def test_topology_molecules_round_trip():
    """
    Test round-trips between OEMol and Topology
    """
    # Create a test set of molecules.
    molecules = [ createOEMolFromIUPAC(name) for name in IUPAC_molecule_names ]
    # Test round-trips.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
    for molecule in molecules:
        # Create topology from molecule.
        topology = generateTopologyFromOEMol(molecule)
        # Create molecule from topology.
        residues = [residue for residue in topology.residues()]
        molecule2 = generateOEMolFromTopologyResidue(residues[0])
        # Create topology form molecule.
        topology2 = generateTopologyFromOEMol(molecule2)
        # Create molecule from topology with geometry.
        residues2 = [residue for residue in topology2.residues()]
        molecule3 = generateOEMolFromTopologyResidue(residues2[0], geometry=True)
        # Create molecule from topology with Tripos atom names
        molecule4 = generateOEMolFromTopologyResidue(residues2[0], tripos_atom_names=True)
Exemplo n.º 11
0
def test_system_generator():
    """
    Test SystemGenerator.
    """
    import openmmtools
    from functools import partial
    # Vacuum tests.
    ffxmls = ['amber99sbildn.xml']
    forcefield_kwargs = {
        'nonbondedMethod': NoCutoff,
        'implicitSolvent': None,
        'constraints': None
    }
    for testsystem_name in ['AlanineDipeptideVacuum']:
        constructor = getattr(openmmtools.testsystems, testsystem_name)
        testsystem = constructor()
        f = partial(check_system_generator, ffxmls, forcefield_kwargs,
                    testsystem.topology)
        f.description = 'Testing SystemGenerator on %s' % testsystem_name
        yield f
    # Implicit solvent tests.
    ffxmls = ['amber99sbildn.xml', 'amber99_obc.xml']
    forcefield_kwargs = {
        'nonbondedMethod': NoCutoff,
        'implicitSolvent': OBC2,
        'constraints': None
    }
    for testsystem_name in ['AlanineDipeptideImplicit']:
        constructor = getattr(openmmtools.testsystems, testsystem_name)
        testsystem = constructor()
        f = partial(check_system_generator, ffxmls, forcefield_kwargs,
                    testsystem.topology)
        f.description = 'Testing SystemGenerator on %s' % testsystem_name
        yield f
    # Small molecule tests.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml")
    ffxmls = [gaff_xml_filename]
    forcefield_kwargs = {
        'nonbondedMethod': NoCutoff,
        'implicitSolvent': None,
        'constraints': None
    }
    for name in IUPAC_molecule_names:
        molecule = createOEMolFromIUPAC(name)
        topology = generateTopologyFromOEMol(molecule)
        f = partial(check_system_generator,
                    ffxmls,
                    forcefield_kwargs,
                    topology,
                    use_gaff=True)
        f.description = 'Testing SystemGenerator on %s' % name
        yield f
Exemplo n.º 12
0
def createSystemFromIUPAC(iupac_name):
    """
    Create an openmm system out of an oemol

    Parameters
    ----------
    iupac_name : str
        IUPAC name

    Returns
    -------
    molecule : openeye.OEMol
        OEMol molecule
    system : openmm.System object
        OpenMM system
    positions : [n,3] np.array of floats
        Positions
    topology : openmm.app.Topology object
        Topology
    """
    from perses.utils.data import get_data_filename
    from perses.utils.openeye import extractPositionsFromOEMol
    # Create OEMol
    molecule = iupac_to_oemol(iupac_name)

    # Generate a topology.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    topology = generateTopologyFromOEMol(molecule)

    # Initialize a forcefield with GAFF.
    # TODO: Fix path for `gaff.xml` since it is not yet distributed with OpenMM
    from simtk.openmm.app import ForceField
    gaff_xml_filename = get_data_filename('data/gaff.xml')
    forcefield = ForceField(gaff_xml_filename)

    # Generate template and parameters.
    from openmoltools.forcefield_generators import generateResidueTemplate
    [template, ffxml] = generateResidueTemplate(molecule)

    # Register the template.
    forcefield.registerResidueTemplate(template)

    # Add the parameters.
    forcefield.loadFile(StringIO(ffxml))

    # Create the system.
    system = forcefield.createSystem(topology, removeCMMotion=False)

    # Extract positions
    positions = extractPositionsFromOEMol(molecule)

    return (molecule, system, positions, topology)
Exemplo n.º 13
0
def createSystemFromIUPAC(iupac_name):
    """
    Create an openmm system out of an oemol

    Parameters
    ----------
    iupac_name : str
        IUPAC name

    Returns
    -------
    molecule : openeye.OEMol
        OEMol molecule
    system : openmm.System object
        OpenMM system
    positions : [n,3] np.array of floats
        Positions
    topology : openmm.app.Topology object
        Topology
    """

    # Create OEMol
    molecule = createOEMolFromIUPAC(iupac_name)

    # Generate a topology.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    topology = generateTopologyFromOEMol(molecule)

    # Initialize a forcefield with GAFF.
    # TODO: Fix path for `gaff.xml` since it is not yet distributed with OpenMM
    from simtk.openmm.app import ForceField
    gaff_xml_filename = get_data_filename('data/gaff.xml')
    forcefield = ForceField(gaff_xml_filename)

    # Generate template and parameters.
    from openmoltools.forcefield_generators import generateResidueTemplate
    [template, ffxml] = generateResidueTemplate(molecule)

    # Register the template.
    forcefield.registerResidueTemplate(template)

    # Add the parameters.
    forcefield.loadFile(StringIO(ffxml))

    # Create the system.
    system = forcefield.createSystem(topology)

    # Extract positions
    positions = extractPositionsFromOEMOL(molecule)

    return (molecule, system, positions, topology)
def test_atom_topology_index():
    """
    Make sure that generateOEMolFromTopologyResidue adds the topology_index data
    """
    # Create a test set of molecules.
    molecules = [ createOEMolFromIUPAC(name) for name in IUPAC_molecule_names ]
    from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
    topologies = [generateTopologyFromOEMol(molecule) for molecule in molecules]
    for topology in topologies:
        residue = list(topology.residues())[0] #there is only one residue
        regenerated_mol = generateOEMolFromTopologyResidue(residue)
        for i, top_atom in enumerate(topology.atoms()):
            oeatom = regenerated_mol.GetAtom(oechem.OEHasAtomIdx(top_atom.index))
            assert oeatom.GetData("topology_index")==top_atom.index
Exemplo n.º 15
0
def test_topology_molecules_round_trip():
    """
    Test round-trips between OEMol and Topology
    """
    # Create a test set of molecules.
    molecules = [createOEMolFromIUPAC(name) for name in IUPAC_molecule_names]
    # Test round-trips.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
    for molecule in molecules:
        # Create topology from molecule.
        topology = generateTopologyFromOEMol(molecule)
        # Create molecule from topology.
        residues = [residue for residue in topology.residues()]
        molecule2 = generateOEMolFromTopologyResidue(residues[0])
        # Create topology form molecule.
        topology2 = generateTopologyFromOEMol(molecule2)
        # Create molecule from topology with geometry.
        residues2 = [residue for residue in topology2.residues()]
        molecule3 = generateOEMolFromTopologyResidue(residues2[0],
                                                     geometry=True)
        # Create molecule from topology with Tripos atom names
        molecule4 = generateOEMolFromTopologyResidue(residues2[0],
                                                     tripos_atom_names=True)
Exemplo n.º 16
0
def generate_ligand_topologies_and_positions(ligand_filename):
    """
    Generate the topologies and positions for ligand-only systems

    Parameters
    ----------
    ligand_filename : str
        The name of the file containing the ligands in any OpenEye supported format

    Returns
    -------
    ligand_topologies : dict of str: md.Topology
        A dictionary of the ligand topologies generated from the file indexed by SMILES strings
    ligand_positions_dict : dict of str: unit.Quantity array
        A dictionary of the corresponding positions, indexed by SMILES strings
    """
    ifs = oechem.oemolistream()
    ifs.open(ligand_filename)

    # get the list of molecules
    mol_list = [oechem.OEMol(mol) for mol in ifs.GetOEMols()]

    for idx, mol in enumerate(mol_list):
        mol.SetTitle("MOL{}".format(idx))
        oechem.OETriposAtomNames(mol)

    mol_dict = {oechem.OEMolToSmiles(mol): mol for mol in mol_list}

    ligand_topology_dict = {
        smiles: forcefield_generators.generateTopologyFromOEMol(mol)
        for smiles, mol in mol_dict.items()
    }

    ligand_topologies = {}
    ligand_positions_dict = {}

    for smiles, ligand_topology in ligand_topology_dict.items():
        ligand_md_topology = md.Topology.from_openmm(ligand_topology)

        ligand_topologies[smiles] = ligand_md_topology

        ligand_positions = extractPositionsFromOEMol(mol_dict[smiles])

        ligand_positions_dict[smiles] = ligand_positions

    return ligand_topologies, ligand_positions_dict
Exemplo n.º 17
0
def test_atom_topology_index():
    """
    Make sure that generateOEMolFromTopologyResidue adds the topology_index data
    """
    # Create a test set of molecules.
    molecules = [createOEMolFromIUPAC(name) for name in IUPAC_molecule_names]
    from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
    topologies = [
        generateTopologyFromOEMol(molecule) for molecule in molecules
    ]
    for topology in topologies:
        residue = list(topology.residues())[0]  #there is only one residue
        regenerated_mol = generateOEMolFromTopologyResidue(residue)
        for i, top_atom in enumerate(topology.atoms()):
            oeatom = regenerated_mol.GetAtom(
                oechem.OEHasAtomIdx(top_atom.index))
            assert oeatom.GetData("topology_index") == top_atom.index
def check_system_generator(ffxmls, forcefield_kwargs, system_name, **kwargs):
    """
    Check SystemGenerator on a specific topology.
    """
    import openmmtools
    try:
        constructor = getattr(openmmtools.testsystems, system_name)
        testsystem = constructor()
        topology = testsystem.topology
    except AttributeError:
        if not HAVE_OE:
            from nose.plugins.skip import SkipTest
            raise SkipTest('Cannot test openeye module without OpenEye tools.\n')
        molecule = createOEMolFromIUPAC(system_name)
        topology = forcefield_generators.generateTopologyFromOEMol(molecule)
    system_generator = forcefield_generators.SystemGenerator(ffxmls,
                                                     forcefield_kwargs=forcefield_kwargs, **kwargs)
    system_generator.createSystem(topology)
Exemplo n.º 19
0
def generate_ligand_topologies_and_positions(ligand_filename):
    """
    Generate the topologies and positions for ligand-only systems

    Parameters
    ----------
    ligand_filename : str
        The name of the file containing the ligands in any OpenEye supported format

    Returns
    -------
    ligand_topologies : dict of str: md.Topology
        A dictionary of the ligand topologies generated from the file indexed by SMILES strings
    ligand_positions_dict : dict of str: unit.Quantity array
        A dictionary of the corresponding positions, indexed by SMILES strings
    """
    ifs = oechem.oemolistream()
    ifs.open(ligand_filename)

    # get the list of molecules
    mol_list = [oechem.OEMol(mol) for mol in ifs.GetOEMols()]

    for idx, mol in enumerate(mol_list):
        mol.SetTitle("MOL{}".format(idx))
        oechem.OETriposAtomNames(mol)

    mol_dict = {oechem.OEMolToSmiles(mol) : mol for mol in mol_list}

    ligand_topology_dict = {smiles : forcefield_generators.generateTopologyFromOEMol(mol) for smiles, mol in mol_dict.items()}

    ligand_topologies = {}
    ligand_positions_dict = {}

    for smiles, ligand_topology in ligand_topology_dict.items():
        ligand_md_topology = md.Topology.from_openmm(ligand_topology)

        ligand_topologies[smiles] = ligand_md_topology

        ligand_positions = extractPositionsFromOEMOL(mol_dict[smiles])

        ligand_positions_dict[smiles] = ligand_positions

    return ligand_topologies, ligand_positions_dict
def check_system_generator(ffxmls, forcefield_kwargs, system_name, **kwargs):
    """
    Check SystemGenerator on a specific topology.
    """
    import openmmtools
    try:
        constructor = getattr(openmmtools.testsystems, system_name)
        testsystem = constructor()
        topology = testsystem.topology
    except AttributeError:
        if not HAVE_OE:
            from nose.plugins.skip import SkipTest
            raise SkipTest(
                'Cannot test openeye module without OpenEye tools.\n')
        molecule = createOEMolFromIUPAC(system_name)
        topology = forcefield_generators.generateTopologyFromOEMol(molecule)
    system_generator = forcefield_generators.SystemGenerator(
        ffxmls, forcefield_kwargs=forcefield_kwargs, **kwargs)
    system_generator.createSystem(topology)
Exemplo n.º 21
0
def OEMol_to_omm_ff(molecule, system_generator):
    """
    Convert an openeye.oechem.OEMol to a openmm system, positions and topology

    Parameters
    ----------
    oemol : openeye.oechem.OEMol object
        input molecule to convert
    system_generator : openmmforcefields.generators.SystemGenerator

    Returns
    -------
    system : openmm.system
    positions : openmm.positions
    topology : openmm.topology

    """
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    topology = generateTopologyFromOEMol(molecule)
    system = system_generator.create_system(topology)
    positions = extractPositionsFromOEMol(molecule)

    return system, positions, topology
Exemplo n.º 22
0
    def test_generate_Topology_and_OEMol(self):
        """
        Test round-trip from OEMol >> Topology >> OEMol
        """
        from openmoltools.forcefield_generators import generateTopologyFromOEMol, generateOEMolFromTopologyResidue
        from openeye import oechem, oeiupac
        for molecule_name in IUPAC_molecule_names:
            molecule1 = createOEMolFromIUPAC(molecule_name)

            # Generate Topology from OEMol
            topology = generateTopologyFromOEMol(molecule1)
            # Check resulting Topology.
            residues = [residue for residue in topology.residues()]
            self.assertEqual(len(residues), 1)
            self.assertEqual(residues[0].name, molecule1.GetTitle())
            for (top_atom, mol_atom) in zip(topology.atoms(),
                                            molecule1.GetAtoms()):
                self.assertEqual(top_atom.name, mol_atom.GetName())
            for (top_bond, mol_bond) in zip(topology.bonds(),
                                            molecule1.GetBonds()):
                self.assertEqual(top_bond[0].name, mol_bond.GetBgn().GetName())
                self.assertEqual(top_bond[1].name, mol_bond.GetEnd().GetName())

            # Generate OEMol from Topology
            molecule2 = generateOEMolFromTopologyResidue(residues[0])
            # Check resulting molecule.
            self.assertEqual(molecule1.GetTitle(), molecule2.GetTitle())
            for (atom1, atom2) in zip(molecule1.GetAtoms(),
                                      molecule2.GetAtoms()):
                self.assertEqual(atom1.GetName(), atom2.GetName())
                self.assertEqual(atom1.GetAtomicNum(), atom2.GetAtomicNum())
            for (bond1, bond2) in zip(molecule1.GetBonds(),
                                      molecule2.GetBonds()):
                self.assertEqual(bond1.GetBgn().GetName(),
                                 bond2.GetBgn().GetName())
                self.assertEqual(bond1.GetEnd().GetName(),
                                 bond2.GetEnd().GetName())
def test_system_generator():
    """
    Test SystemGenerator.
    """
    import openmmtools
    from functools import partial

    # Vacuum tests.
    ffxmls = ["amber99sbildn.xml"]
    forcefield_kwargs = {"nonbondedMethod": NoCutoff, "implicitSolvent": None, "constraints": None}
    for testsystem_name in ["AlanineDipeptideVacuum"]:
        constructor = getattr(openmmtools.testsystems, testsystem_name)
        testsystem = constructor()
        f = partial(check_system_generator, ffxmls, forcefield_kwargs, testsystem.topology)
        f.description = "Testing SystemGenerator on %s" % testsystem_name
        yield f
    # Implicit solvent tests.
    ffxmls = ["amber99sbildn.xml", "amber99_obc.xml"]
    forcefield_kwargs = {"nonbondedMethod": NoCutoff, "implicitSolvent": OBC2, "constraints": None}
    for testsystem_name in ["AlanineDipeptideImplicit"]:
        constructor = getattr(openmmtools.testsystems, testsystem_name)
        testsystem = constructor()
        f = partial(check_system_generator, ffxmls, forcefield_kwargs, testsystem.topology)
        f.description = "Testing SystemGenerator on %s" % testsystem_name
        yield f
    # Small molecule tests.
    from openmoltools.forcefield_generators import generateTopologyFromOEMol

    gaff_xml_filename = utils.get_data_filename("parameters/gaff.xml")
    ffxmls = [gaff_xml_filename]
    forcefield_kwargs = {"nonbondedMethod": NoCutoff, "implicitSolvent": None, "constraints": None}
    for name in IUPAC_molecule_names:
        molecule = createOEMolFromIUPAC(name)
        topology = generateTopologyFromOEMol(molecule)
        f = partial(check_system_generator, ffxmls, forcefield_kwargs, topology, use_gaff=True)
        f.description = "Testing SystemGenerator on %s" % name
        yield f
Exemplo n.º 24
0
def generate_complex_topologies_and_positions(ligand_filename,
                                              protein_pdb_filename):
    """
    Generate the topologies and positions for complex phase simulations, given an input ligand file (in supported openeye
    format) and protein pdb file. Note that the input ligand file should have coordinates placing the ligand in the binding
    site.

    Parameters
    ----------
    ligand_filename : str
        Name of the file containing ligands
    protein_pdb_filename : str
        Name of the protein pdb file

    Returns
    -------
    complex_topologies_dict : dict of smiles: md.topology
        Dictionary of topologies for various complex systems
    complex_positions_dict : dict of smiles:  [n, 3] array of Quantity
        Positions for corresponding complexes
    """
    ifs = oechem.oemolistream()
    ifs.open(ligand_filename)

    # get the list of molecules
    mol_list = [oechem.OEMol(mol) for mol in ifs.GetOEMols()]

    for idx, mol in enumerate(mol_list):
        mol.SetTitle("MOL{}".format(idx))
        oechem.OETriposAtomNames(mol)

    mol_dict = {oechem.OEMolToSmiles(mol): mol for mol in mol_list}

    ligand_topology_dict = {
        smiles: forcefield_generators.generateTopologyFromOEMol(mol)
        for smiles, mol in mol_dict.items()
    }

    protein_pdbfile = open(protein_pdb_filename, 'r')
    pdb_file = app.PDBFile(protein_pdbfile)
    protein_pdbfile.close()
    receptor_positions = pdb_file.positions
    receptor_topology = pdb_file.topology
    receptor_md_topology = md.Topology.from_openmm(receptor_topology)

    n_receptor_atoms = receptor_md_topology.n_atoms

    complex_topologies = {}
    complex_positions_dict = {}

    for smiles, ligand_topology in ligand_topology_dict.items():
        ligand_md_topology = md.Topology.from_openmm(ligand_topology)

        n_complex_atoms = ligand_md_topology.n_atoms + n_receptor_atoms
        copy_receptor_md_topology = copy.deepcopy(receptor_md_topology)

        complex_positions = unit.Quantity(np.zeros([n_complex_atoms, 3]),
                                          unit=unit.nanometers)

        complex_topology = copy_receptor_md_topology.join(ligand_md_topology)

        complex_topologies[smiles] = complex_topology

        ligand_positions = extractPositionsFromOEMol(mol_dict[smiles])

        complex_positions[:n_receptor_atoms, :] = receptor_positions
        complex_positions[n_receptor_atoms:, :] = ligand_positions

        complex_positions_dict[smiles] = complex_positions

    return complex_topologies, complex_positions_dict
Exemplo n.º 25
0
    def __init__(self, molecules: List[str], output_filename: str, ncmc_switching_times: Dict[str, int], equilibrium_steps: Dict[str, int], timestep: unit.Quantity, initial_molecule: str=None, geometry_options: Dict=None):
        self._molecules = [SmallMoleculeSetProposalEngine.canonicalize_smiles(molecule) for molecule in molecules]
        environments = ['explicit', 'vacuum']
        temperature = 298.15 * unit.kelvin
        pressure = 1.0 * unit.atmospheres
        constraints = app.HBonds
        self._storage = NetCDFStorage(output_filename)
        self._ncmc_switching_times = ncmc_switching_times
        self._n_equilibrium_steps = equilibrium_steps
        self._geometry_options = geometry_options

        # Create a system generator for our desired forcefields.
        from perses.rjmc.topology_proposal import SystemGenerator
        system_generators = dict()
        from pkg_resources import resource_filename
        gaff_xml_filename = resource_filename('perses', 'data/gaff.xml')
        barostat = openmm.MonteCarloBarostat(pressure, temperature)
        system_generators['explicit'] = SystemGenerator([gaff_xml_filename, 'tip3p.xml'],
                                                        forcefield_kwargs={'nonbondedMethod': app.PME,
                                                                           'nonbondedCutoff': 9.0 * unit.angstrom,
                                                                           'implicitSolvent': None,
                                                                           'constraints': constraints,
                                                                           'ewaldErrorTolerance': 1e-5,
                                                                           'hydrogenMass': 3.0*unit.amu},
                                                        barostat=barostat)
        system_generators['vacuum'] = SystemGenerator([gaff_xml_filename],
                                                      forcefield_kwargs={'nonbondedMethod': app.NoCutoff,
                                                                         'implicitSolvent': None,
                                                                         'constraints': constraints,
                                                                         'hydrogenMass': 3.0*unit.amu})

        #
        # Create topologies and positions
        #
        topologies = dict()
        positions = dict()

        from openmoltools import forcefield_generators
        forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml')
        forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator)

        # Create molecule in vacuum.
        from perses.tests.utils import createOEMolFromSMILES, extractPositionsFromOEMOL
        if initial_molecule:
            smiles = initial_molecule
        else:
            smiles = np.random.choice(molecules)
        molecule = createOEMolFromSMILES(smiles)
        topologies['vacuum'] = forcefield_generators.generateTopologyFromOEMol(molecule)
        positions['vacuum'] = extractPositionsFromOEMOL(molecule)

        # Create molecule in solvent.
        modeller = app.Modeller(topologies['vacuum'], positions['vacuum'])
        modeller.addSolvent(forcefield, model='tip3p', padding=9.0 * unit.angstrom)
        topologies['explicit'] = modeller.getTopology()
        positions['explicit'] = modeller.getPositions()

        # Set up the proposal engines.
        proposal_metadata = {}
        proposal_engines = dict()

        for environment in environments:
            proposal_engines[environment] = SmallMoleculeSetProposalEngine(self._molecules,
                                                                               system_generators[environment])

        # Generate systems
        systems = dict()
        for environment in environments:
            systems[environment] = system_generators[environment].build_system(topologies[environment])

        # Define thermodynamic state of interest.

        thermodynamic_states = dict()
        thermodynamic_states['explicit'] = states.ThermodynamicState(system=systems['explicit'],
                                                                     temperature=temperature, pressure=pressure)
        thermodynamic_states['vacuum'] = states.ThermodynamicState(system=systems['vacuum'], temperature=temperature)

        # Create SAMS samplers
        from perses.samplers.samplers import ExpandedEnsembleSampler, SAMSSampler
        mcmc_samplers = dict()
        exen_samplers = dict()
        sams_samplers = dict()
        for environment in environments:
            storage = NetCDFStorageView(self._storage, envname=environment)

            if self._geometry_options:
                n_torsion_divisions = self._geometry_options['n_torsion_divsions'][environment]
                use_sterics = self._geometry_options['use_sterics'][environment]

            else:
                n_torsion_divisions = 180
                use_sterics = False

            geometry_engine = geometry.FFAllAngleGeometryEngine(storage=storage, n_torsion_divisions=n_torsion_divisions, use_sterics=use_sterics)
            move = mcmc.LangevinSplittingDynamicsMove(timestep=timestep, splitting="V R O R V",
                                                       n_restart_attempts=10)
            chemical_state_key = proposal_engines[environment].compute_state_key(topologies[environment])
            if environment == 'explicit':
                sampler_state = states.SamplerState(positions=positions[environment],
                                                    box_vectors=systems[environment].getDefaultPeriodicBoxVectors())
            else:
                sampler_state = states.SamplerState(positions=positions[environment])
            mcmc_samplers[environment] = mcmc.MCMCSampler(thermodynamic_states[environment], sampler_state, move)


            exen_samplers[environment] = ExpandedEnsembleSampler(mcmc_samplers[environment], topologies[environment],
                                                                 chemical_state_key, proposal_engines[environment],
                                                                 geometry_engine,
                                                                 options={'nsteps': self._ncmc_switching_times[environment]}, storage=storage, ncmc_write_interval=self._ncmc_switching_times[environment])
            exen_samplers[environment].verbose = True
            sams_samplers[environment] = SAMSSampler(exen_samplers[environment], storage=storage)
            sams_samplers[environment].verbose = True

        # Create test MultiTargetDesign sampler.
        from perses.samplers.samplers import MultiTargetDesign
        target_samplers = {sams_samplers['explicit']: 1.0, sams_samplers['vacuum']: -1.0}
        designer = MultiTargetDesign(target_samplers, storage=self._storage)

        # Store things.
        self.molecules = molecules
        self.environments = environments
        self.topologies = topologies
        self.positions = positions
        self.system_generators = system_generators
        self.proposal_engines = proposal_engines
        self.thermodynamic_states = thermodynamic_states
        self.mcmc_samplers = mcmc_samplers
        self.exen_samplers = exen_samplers
        self.sams_samplers = sams_samplers
        self.designer = designer
Exemplo n.º 26
0
def generate_vacuum_topology_proposal(current_mol_name="benzene", proposed_mol_name="toluene", forcefield_kwargs=None, system_generator_kwargs=None):
    """
    Generate a test vacuum topology proposal, current positions, and new positions triplet from two IUPAC molecule names.

    Constraints are added to the system by default. To override this, set ``forcefield_kwargs = None``.

    Parameters
    ----------
    current_mol_name : str, optional
        name of the first molecule
    proposed_mol_name : str, optional
        name of the second molecule
    forcefield_kwargs : dict, optional, default=None
        Additional arguments to ForceField in addition to
        'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff
    system_generator_kwargs : dict, optional, default=None
        Dict passed onto SystemGenerator

    Returns
    -------
    topology_proposal : perses.rjmc.topology_proposal
        The topology proposal representing the transformation
    current_positions : np.array, unit-bearing
        The positions of the initial system
    new_positions : np.array, unit-bearing
        The positions of the new system
    """
    from openmoltools import forcefield_generators
    from perses.tests.utils import createOEMolFromIUPAC, createSystemFromIUPAC, get_data_filename

    gaff_filename = get_data_filename('data/gaff.xml')
    default_forcefield_kwargs = {'removeCMMotion': False, 'nonbondedMethod': app.NoCutoff, 'constraints' : app.HBonds}
    forcefield_kwargs = default_forcefield_kwargs.update(forcefield_kwargs) if (forcefield_kwargs is not None) else default_forcefield_kwargs
    system_generator_kwargs = system_generator_kwargs if (system_generator_kwargs is not None) else dict()
    system_generator = SystemGenerator([gaff_filename, 'amber99sbildn.xml', 'tip3p.xml'],
        forcefield_kwargs=forcefield_kwargs,
        **system_generator_kwargs)

    old_oemol = createOEMolFromIUPAC(current_mol_name)
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    old_topology = generateTopologyFromOEMol(old_oemol)
    old_positions = extractPositionsFromOEMOL(old_oemol)
    old_smiles = oechem.OEMolToSmiles(old_oemol)
    old_system = system_generator.build_system(old_topology)

    new_oemol = createOEMolFromIUPAC(proposed_mol_name)
    new_smiles = oechem.OEMolToSmiles(new_oemol)

    geometry_engine = geometry.FFAllAngleGeometryEngine()
    proposal_engine = SmallMoleculeSetProposalEngine(
        [old_smiles, new_smiles], system_generator, residue_name=current_mol_name)

    #generate topology proposal
    topology_proposal = proposal_engine.propose(old_system, old_topology, current_mol=old_oemol, proposed_mol=new_oemol)

    # show atom mapping
    filename = str(current_mol_name)+str(proposed_mol_name)+'.pdf'
    render_atom_mapping(filename, old_oemol, new_oemol, topology_proposal.new_to_old_atom_map)

    #generate new positions with geometry engine
    new_positions, _ = geometry_engine.propose(topology_proposal, old_positions, beta)

    # DEBUG: Zero out bonds and angles for one system
    #print('Zeroing bonds of old system')
    #for force in topology_proposal.old_system.getForces():
    #    if force.__class__.__name__ == 'HarmonicAngleForce':
    #        for index in range(force.getNumAngles()):
    #            p1, p2, p3, angle, K = force.getAngleParameters(index)
    #            K *= 0.0
    #            force.setAngleParameters(index, p1, p2, p3, angle, K)
    #    if False and force.__class__.__name__ == 'HarmonicBondForce':
    #        for index in range(force.getNumBonds()):
    #            p1, p2, r0, K = force.getBondParameters(index)
    #            K *= 0.0
    #            force.setBondParameters(index, p1, p2, r0, K)

    # DEBUG : Box vectors
    #box_vectors = np.eye(3) * 100 * unit.nanometers
    #topology_proposal.old_system.setDefaultPeriodicBoxVectors(box_vectors[0,:], box_vectors[1,:], box_vectors[2,:])
    #topology_proposal.new_system.setDefaultPeriodicBoxVectors(box_vectors[0,:], box_vectors[1,:], box_vectors[2,:])

    return topology_proposal, old_positions, new_positions
Exemplo n.º 27
0
    def __init__(self,
                 protein_pdb_filename,
                 ligand_file,
                 old_ligand_index,
                 new_ligand_index,
                 forcefield_files,
                 pressure=1.0 * unit.atmosphere,
                 temperature=300.0 * unit.kelvin,
                 solvent_padding=9.0 * unit.angstroms):
        """
        Initialize a NonequilibriumFEPSetup object

        Parameters
        ----------
        protein_pdb_filename : str
            The name of the protein pdb file
        ligand_file : str
            the name of the ligand file (any openeye supported format)
        ligand_smiles : list of two str
            The SMILES strings representing the two ligands
        forcefield_files : list of str
            The list of ffxml files that contain the forcefields that will be used
        pressure : Quantity, units of pressure
            Pressure to use in the barostat
        temperature : Quantity, units of temperature
            Temperature to use for the Langevin integrator
        solvent_padding : Quantity, units of length
            The amount of padding to use when adding solvent
        """
        self._protein_pdb_filename = protein_pdb_filename
        self._pressure = pressure
        self._temperature = temperature
        self._barostat_period = 50
        self._padding = solvent_padding

        self._ligand_file = ligand_file
        self._old_ligand_index = old_ligand_index
        self._new_ligand_index = new_ligand_index

        self._old_ligand_oemol = self.load_sdf(self._ligand_file,
                                               index=self._old_ligand_index)
        self._new_ligand_oemol = self.load_sdf(self._ligand_file,
                                               index=self._new_ligand_index)

        self._old_ligand_positions = extractPositionsFromOEMOL(
            self._old_ligand_oemol)

        ffxml = forcefield_generators.generateForceFieldFromMolecules(
            [self._old_ligand_oemol, self._new_ligand_oemol])

        self._old_ligand_oemol.SetTitle("MOL")
        self._new_ligand_oemol.SetTitle("MOL")

        self._new_ligand_smiles = oechem.OECreateSmiString(
            self._new_ligand_oemol,
            oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)
        #self._old_ligand_smiles = '[H]c1c(c(c(c(c1N([H])c2nc3c(c(n2)OC([H])([H])C4(C(C(C(C(C4([H])[H])([H])[H])([H])[H])([H])[H])([H])[H])[H])nc(n3[H])[H])[H])[H])S(=O)(=O)C([H])([H])[H])[H]'
        self._old_ligand_smiles = oechem.OECreateSmiString(
            self._old_ligand_oemol,
            oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)

        print(self._new_ligand_smiles)
        print(self._old_ligand_smiles)

        self._old_ligand_topology = forcefield_generators.generateTopologyFromOEMol(
            self._old_ligand_oemol)
        self._old_ligand_md_topology = md.Topology.from_openmm(
            self._old_ligand_topology)
        self._new_ligand_topology = forcefield_generators.generateTopologyFromOEMol(
            self._new_ligand_oemol)
        self._new_liands_md_topology = md.Topology.from_openmm(
            self._new_ligand_topology)

        protein_pdbfile = open(self._protein_pdb_filename, 'r')
        pdb_file = app.PDBFile(protein_pdbfile)
        protein_pdbfile.close()

        self._protein_topology_old = pdb_file.topology
        self._protein_md_topology_old = md.Topology.from_openmm(
            self._protein_topology_old)
        self._protein_positions_old = pdb_file.positions
        self._forcefield = app.ForceField(*forcefield_files)
        self._forcefield.loadFile(StringIO(ffxml))

        print("Generated forcefield")

        self._complex_md_topology_old = self._protein_md_topology_old.join(
            self._old_ligand_md_topology)
        self._complex_topology_old = self._complex_md_topology_old.to_openmm()

        n_atoms_complex_old = self._complex_topology_old.getNumAtoms()
        n_atoms_protein_old = self._protein_topology_old.getNumAtoms()

        self._complex_positions_old = unit.Quantity(np.zeros(
            [n_atoms_complex_old, 3]),
                                                    unit=unit.nanometers)
        self._complex_positions_old[:
                                    n_atoms_protein_old, :] = self._protein_positions_old
        self._complex_positions_old[
            n_atoms_protein_old:, :] = self._old_ligand_positions

        if pressure is not None:
            barostat = openmm.MonteCarloBarostat(self._pressure,
                                                 self._temperature,
                                                 self._barostat_period)
            self._system_generator = SystemGenerator(
                forcefield_files,
                barostat=barostat,
                forcefield_kwargs={'nonbondedMethod': app.PME})
        else:
            self._system_generator = SystemGenerator(forcefield_files)

        #self._complex_proposal_engine = TwoMoleculeSetProposalEngine(self._old_ligand_smiles, self._new_ligand_smiles, self._system_generator, residue_name="MOL")
        self._complex_proposal_engine = TwoMoleculeSetProposalEngine(
            self._old_ligand_oemol,
            self._new_ligand_oemol,
            self._system_generator,
            residue_name="MOL")
        self._geometry_engine = FFAllAngleGeometryEngine()

        self._complex_topology_old_solvated, self._complex_positions_old_solvated, self._complex_system_old_solvated = self._solvate_system(
            self._complex_topology_old, self._complex_positions_old)
        self._complex_md_topology_old_solvated = md.Topology.from_openmm(
            self._complex_topology_old_solvated)
        print(self._complex_proposal_engine._smiles_list)

        beta = 1.0 / (kB * temperature)

        self._complex_topology_proposal = self._complex_proposal_engine.propose(
            self._complex_system_old_solvated,
            self._complex_topology_old_solvated)
        self._complex_positions_new_solvated, _ = self._geometry_engine.propose(
            self._complex_topology_proposal,
            self._complex_positions_old_solvated, beta)

        #now generate the equivalent objects for the solvent phase. First, generate the ligand-only topologies and atom map
        self._solvent_topology_proposal, self._old_solvent_positions = self._generate_ligand_only_topologies(
            self._complex_positions_old_solvated,
            self._complex_positions_new_solvated)
        self._new_solvent_positions, _ = self._geometry_engine.propose(
            self._solvent_topology_proposal, self._old_solvent_positions, beta)
    def __init__(
            self,
            receptor_filename,
            ligand_filename,
            mutation_chain_id,
            mutation_residue_id,
            proposed_residue,
            phase='complex',
            conduct_endstate_validation=False,
            ligand_index=0,
            forcefield_files=[
                'amber14/protein.ff14SB.xml', 'amber14/tip3p.xml'
            ],
            barostat=openmm.MonteCarloBarostat(1.0 * unit.atmosphere,
                                               temperature, 50),
            forcefield_kwargs={
                'removeCMMotion': False,
                'ewaldErrorTolerance': 1e-4,
                'nonbondedMethod': app.PME,
                'constraints': app.HBonds,
                'hydrogenMass': 4 * unit.amus
            },
            small_molecule_forcefields='gaff-2.11',
            **kwargs):
        """
        arguments
            receptor_filename : str
                path to receptor; .pdb
            ligand_filename : str
                path to ligand of interest; .sdf or .pdb
            mutation_chain_id : str
                name of the chain to be mutated
            mutation_residue_id : str
                residue id to change
            proposed_residue : str
                three letter code of the residue to mutate to
            phase : str, default complex
                if phase == vacuum, then the complex will not be solvated with water; else, it will be solvated with tip3p
            conduct_endstate_validation : bool, default True
                whether to conduct an endstate validation of the hybrid topology factory
            ligand_index : int, default 0
                which ligand to use
            forcefield_files : list of str, default ['amber14/protein.ff14SB.xml', 'amber14/tip3p.xml']
                forcefield files for proteins and solvent
            barostat : openmm.MonteCarloBarostat, default openmm.MonteCarloBarostat(1.0 * unit.atmosphere, 300 * unit.kelvin, 50)
                barostat to use
            forcefield_kwargs : dict, default {'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'nonbondedMethod': app.NoCutoff, 'constraints' : app.HBonds, 'hydrogenMass' : 4 * unit.amus}
                forcefield kwargs for system parametrization
            small_molecule_forcefields : str, default 'gaff-2.11'
                the forcefield string for small molecule parametrization

        TODO : allow argument for separate apo structure if it exists separately
               allow argument for specator ligands besides the 'ligand_filename'
        """
        from openforcefield.topology import Molecule
        from openmmforcefields.generators import SystemGenerator

        # first thing to do is make a complex and apo...
        pdbfile = open(receptor_filename, 'r')
        pdb = app.PDBFile(pdbfile)
        pdbfile.close()
        receptor_positions, receptor_topology, receptor_md_topology = pdb.positions, pdb.topology, md.Topology.from_openmm(
            pdb.topology)
        receptor_topology = receptor_md_topology.to_openmm()
        receptor_n_atoms = receptor_md_topology.n_atoms

        molecules = []
        ligand_mol = createOEMolFromSDF(ligand_filename, index=ligand_index)
        ligand_mol = generate_unique_atom_names(ligand_mol)
        molecules.append(
            Molecule.from_openeye(ligand_mol, allow_undefined_stereo=False))
        ligand_positions, ligand_topology = extractPositionsFromOEMol(
            ligand_mol), forcefield_generators.generateTopologyFromOEMol(
                ligand_mol)
        ligand_md_topology = md.Topology.from_openmm(ligand_topology)
        ligand_n_atoms = ligand_md_topology.n_atoms

        #now create a complex
        complex_md_topology = receptor_md_topology.join(ligand_md_topology)
        complex_topology = complex_md_topology.to_openmm()
        complex_positions = unit.Quantity(np.zeros(
            [receptor_n_atoms + ligand_n_atoms, 3]),
                                          unit=unit.nanometers)
        complex_positions[:receptor_n_atoms, :] = receptor_positions
        complex_positions[receptor_n_atoms:, :] = ligand_positions

        #now for a system_generator
        self.system_generator = SystemGenerator(
            forcefields=forcefield_files,
            barostat=barostat,
            forcefield_kwargs=forcefield_kwargs,
            small_molecule_forcefield=small_molecule_forcefields,
            molecules=molecules,
            cache=None)

        #create complex and apo inputs...
        complex_topology, complex_positions, complex_system = self._solvate(
            complex_topology, complex_positions, 'tip3p', phase=phase)
        apo_topology, apo_positions, apo_system = self._solvate(
            receptor_topology, receptor_positions, 'tip3p', phase='phase')

        geometry_engine = FFAllAngleGeometryEngine(
            metadata=None,
            use_sterics=False,
            n_bond_divisions=100,
            n_angle_divisions=180,
            n_torsion_divisions=360,
            verbose=True,
            storage=None,
            bond_softening_constant=1.0,
            angle_softening_constant=1.0,
            neglect_angles=False,
            use_14_nonbondeds=True)

        #run pipeline...
        htfs = []
        for (top, pos, sys) in zip([complex_topology, apo_topology],
                                   [complex_positions, apo_positions],
                                   [complex_system, apo_system]):
            point_mutation_engine = PointMutationEngine(
                wildtype_topology=top,
                system_generator=self.system_generator,
                chain_id=
                mutation_chain_id,  #denote the chain id allowed to mutate (it's always a string variable)
                max_point_mutants=1,
                residues_allowed_to_mutate=[
                    mutation_residue_id
                ],  #the residue ids allowed to mutate
                allowed_mutations=[
                    (mutation_residue_id, proposed_residue)
                ],  #the residue ids allowed to mutate with the three-letter code allowed to change
                aggregate=True)  #always allow aggregation

            topology_proposal = point_mutation_engine.propose(sys, top)

            new_positions, logp_proposal = geometry_engine.propose(
                topology_proposal, pos, beta)
            logp_reverse = geometry_engine.logp_reverse(
                topology_proposal, new_positions, pos, beta)

            forward_htf = HybridTopologyFactory(
                topology_proposal=topology_proposal,
                current_positions=pos,
                new_positions=new_positions,
                use_dispersion_correction=False,
                functions=None,
                softcore_alpha=None,
                bond_softening_constant=1.0,
                angle_softening_constant=1.0,
                soften_only_new=False,
                neglected_new_angle_terms=[],
                neglected_old_angle_terms=[],
                softcore_LJ_v2=True,
                softcore_electrostatics=True,
                softcore_LJ_v2_alpha=0.85,
                softcore_electrostatics_alpha=0.3,
                softcore_sigma_Q=1.0,
                interpolate_old_and_new_14s=False,
                omitted_terms=None)

            if not topology_proposal.unique_new_atoms:
                assert geometry_engine.forward_final_context_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.forward_final_context_reduced_potential})"
                assert geometry_engine.forward_atoms_with_positions_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's forward atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.forward_atoms_with_positions_reduced_potential})"
                vacuum_added_valence_energy = 0.0
            else:
                added_valence_energy = geometry_engine.forward_final_context_reduced_potential - geometry_engine.forward_atoms_with_positions_reduced_potential

            if not topology_proposal.unique_old_atoms:
                assert geometry_engine.reverse_final_context_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.reverse_final_context_reduced_potential})"
                assert geometry_engine.reverse_atoms_with_positions_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.reverse_atoms_with_positions_reduced_potential})"
                subtracted_valence_energy = 0.0
            else:
                subtracted_valence_energy = geometry_engine.reverse_final_context_reduced_potential - geometry_engine.reverse_atoms_with_positions_reduced_potential

            if conduct_endstate_validation:
                zero_state_error, one_state_error = validate_endstate_energies(
                    forward_htf._topology_proposal,
                    forward_htf,
                    added_valence_energy,
                    subtracted_valence_energy,
                    beta=beta,
                    ENERGY_THRESHOLD=ENERGY_THRESHOLD)
            else:
                pass

            htfs.append(forward_htf)

        self.complex_htf = htfs[0]
        self.apo_htf = htfs[1]
Exemplo n.º 29
0
    def __init__(self, molecules: List[str], output_filename: str, ncmc_switching_times: Dict[str, int], equilibrium_steps: Dict[str, int], timestep: unit.Quantity, initial_molecule: str=None, geometry_options: Dict=None):
        self._molecules = [SmallMoleculeSetProposalEngine.canonicalize_smiles(molecule) for molecule in molecules]
        environments = ['explicit', 'vacuum']
        temperature = 298.15 * unit.kelvin
        pressure = 1.0 * unit.atmospheres
        constraints = app.HBonds
        self._storage = NetCDFStorage(output_filename)
        self._ncmc_switching_times = ncmc_switching_times
        self._n_equilibrium_steps = equilibrium_steps
        self._geometry_options = geometry_options

        # Create a system generator for our desired forcefields.
        from perses.rjmc.topology_proposal import SystemGenerator
        system_generators = dict()
        from pkg_resources import resource_filename
        gaff_xml_filename = resource_filename('perses', 'data/gaff.xml')
        barostat = openmm.MonteCarloBarostat(pressure, temperature)
        system_generators['explicit'] = SystemGenerator([gaff_xml_filename, 'tip3p.xml'],
                                                        forcefield_kwargs={'nonbondedCutoff': 9.0 * unit.angstrom,
                                                                           'implicitSolvent': None,
                                                                           'constraints': constraints,
                                                                           'ewaldErrorTolerance': 1e-5,
                                                                           'hydrogenMass': 3.0*unit.amu}, periodic_forcefield_kwargs = {'nonbondedMethod': app.PME}
                                                        barostat=barostat)
        system_generators['vacuum'] = SystemGenerator([gaff_xml_filename],
                                                      forcefield_kwargs={'implicitSolvent': None,
                                                                         'constraints': constraints,
                                                                         'hydrogenMass': 3.0*unit.amu}, nonperiodic_forcefield_kwargs = {'nonbondedMethod': app.NoCutoff})

        #
        # Create topologies and positions
        #
        topologies = dict()
        positions = dict()

        from openmoltools import forcefield_generators
        forcefield = app.ForceField(gaff_xml_filename, 'tip3p.xml')
        forcefield.registerTemplateGenerator(forcefield_generators.gaffTemplateGenerator)

        # Create molecule in vacuum.
        from perses.utils.openeye import extractPositionsFromOEMol
        from openmoltools.openeye import smiles_to_oemol, generate_conformers
        if initial_molecule:
            smiles = initial_molecule
        else:
            smiles = np.random.choice(molecules)
        molecule = smiles_to_oemol(smiles)
        molecule = generate_conformers(molecule, max_confs=1)
        topologies['vacuum'] = forcefield_generators.generateTopologyFromOEMol(molecule)
        positions['vacuum'] = extractPositionsFromOEMol(molecule)

        # Create molecule in solvent.
        modeller = app.Modeller(topologies['vacuum'], positions['vacuum'])
        modeller.addSolvent(forcefield, model='tip3p', padding=9.0 * unit.angstrom)
        topologies['explicit'] = modeller.getTopology()
        positions['explicit'] = modeller.getPositions()

        # Set up the proposal engines.
        proposal_metadata = {}
        proposal_engines = dict()

        for environment in environments:
            proposal_engines[environment] = SmallMoleculeSetProposalEngine(self._molecules,
                                                                               system_generators[environment])

        # Generate systems
        systems = dict()
        for environment in environments:
            systems[environment] = system_generators[environment].build_system(topologies[environment])

        # Define thermodynamic state of interest.

        thermodynamic_states = dict()
        thermodynamic_states['explicit'] = states.ThermodynamicState(system=systems['explicit'],
                                                                     temperature=temperature, pressure=pressure)
        thermodynamic_states['vacuum'] = states.ThermodynamicState(system=systems['vacuum'], temperature=temperature)

        # Create SAMS samplers
        from perses.samplers.samplers import ExpandedEnsembleSampler, SAMSSampler
        mcmc_samplers = dict()
        exen_samplers = dict()
        sams_samplers = dict()
        for environment in environments:
            storage = NetCDFStorageView(self._storage, envname=environment)

            if self._geometry_options:
                n_torsion_divisions = self._geometry_options['n_torsion_divsions'][environment]
                use_sterics = self._geometry_options['use_sterics'][environment]

            else:
                n_torsion_divisions = 180
                use_sterics = False

            geometry_engine = geometry.FFAllAngleGeometryEngine(storage=storage, n_torsion_divisions=n_torsion_divisions, use_sterics=use_sterics)
            move = mcmc.LangevinSplittingDynamicsMove(timestep=timestep, splitting="V R O R V",
                                                       n_restart_attempts=10)
            chemical_state_key = proposal_engines[environment].compute_state_key(topologies[environment])
            if environment == 'explicit':
                sampler_state = states.SamplerState(positions=positions[environment],
                                                    box_vectors=systems[environment].getDefaultPeriodicBoxVectors())
            else:
                sampler_state = states.SamplerState(positions=positions[environment])
            mcmc_samplers[environment] = mcmc.MCMCSampler(thermodynamic_states[environment], sampler_state, move)


            exen_samplers[environment] = ExpandedEnsembleSampler(mcmc_samplers[environment], topologies[environment],
                                                                 chemical_state_key, proposal_engines[environment],
                                                                 geometry_engine,
                                                                 options={'nsteps': self._ncmc_switching_times[environment]}, storage=storage, ncmc_write_interval=self._ncmc_switching_times[environment])
            exen_samplers[environment].verbose = True
            sams_samplers[environment] = SAMSSampler(exen_samplers[environment], storage=storage)
            sams_samplers[environment].verbose = True

        # Create test MultiTargetDesign sampler.
        from perses.samplers.samplers import MultiTargetDesign
        target_samplers = {sams_samplers['explicit']: 1.0, sams_samplers['vacuum']: -1.0}
        designer = MultiTargetDesign(target_samplers, storage=self._storage)

        # Store things.
        self.molecules = molecules
        self.environments = environments
        self.topologies = topologies
        self.positions = positions
        self.system_generators = system_generators
        self.proposal_engines = proposal_engines
        self.thermodynamic_states = thermodynamic_states
        self.mcmc_samplers = mcmc_samplers
        self.exen_samplers = exen_samplers
        self.sams_samplers = sams_samplers
        self.designer = designer
Exemplo n.º 30
0
def compare_energies(mol_name="naphthalene",
                     ref_mol_name="benzene",
                     atom_expression=['Hybridization'],
                     bond_expression=['Hybridization']):
    """
    Make an atom map where the molecule at either lambda endpoint is identical, and check that the energies are also the same.
    """
    from openmmtools.constants import kB
    from openmmtools import alchemy, states
    from perses.rjmc.topology_proposal import SmallMoleculeSetProposalEngine
    from perses.annihilation.relative import HybridTopologyFactory
    from perses.rjmc.geometry import FFAllAngleGeometryEngine
    import simtk.openmm as openmm
    from perses.utils.openeye import iupac_to_oemol, extractPositionsFromOEMol, generate_conformers
    from perses.utils.openeye import generate_expression
    from openmmforcefields.generators import SystemGenerator
    from openmoltools.forcefield_generators import generateTopologyFromOEMol
    from perses.tests.utils import validate_endstate_energies
    temperature = 300 * unit.kelvin
    # Compute kT and inverse temperature.
    kT = kB * temperature
    beta = 1.0 / kT
    ENERGY_THRESHOLD = 1e-6

    atom_expr, bond_expr = generate_expression(
        atom_expression), generate_expression(bond_expression)

    mol = iupac_to_oemol(mol_name)
    mol = generate_conformers(mol, max_confs=1)

    refmol = iupac_to_oemol(ref_mol_name)
    refmol = generate_conformers(refmol, max_confs=1)

    from openforcefield.topology import Molecule
    molecules = [Molecule.from_openeye(oemol) for oemol in [refmol, mol]]
    barostat = None
    forcefield_files = ['amber14/protein.ff14SB.xml', 'amber14/tip3p.xml']
    forcefield_kwargs = {
        'removeCMMotion': False,
        'ewaldErrorTolerance': 1e-4,
        'nonbondedMethod': app.NoCutoff,
        'constraints': app.HBonds,
        'hydrogenMass': 4 * unit.amus
    }

    system_generator = SystemGenerator(forcefields=forcefield_files,
                                       barostat=barostat,
                                       forcefield_kwargs=forcefield_kwargs,
                                       small_molecule_forcefield='gaff-2.11',
                                       molecules=molecules,
                                       cache=None)

    topology = generateTopologyFromOEMol(refmol)
    system = system_generator.create_system(topology)
    positions = extractPositionsFromOEMol(refmol)

    proposal_engine = SmallMoleculeSetProposalEngine([refmol, mol],
                                                     system_generator)
    proposal = proposal_engine.propose(system,
                                       topology,
                                       atom_expr=atom_expr,
                                       bond_expr=bond_expr)
    geometry_engine = FFAllAngleGeometryEngine()
    new_positions, _ = geometry_engine.propose(
        proposal, positions, beta=beta, validate_energy_bookkeeping=False)
    _ = geometry_engine.logp_reverse(proposal, new_positions, positions, beta)
    #make a topology proposal with the appropriate data:

    factory = HybridTopologyFactory(proposal, positions, new_positions)
    if not proposal.unique_new_atoms:
        assert geometry_engine.forward_final_context_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.forward_final_context_reduced_potential})"
        assert geometry_engine.forward_atoms_with_positions_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's forward atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.forward_atoms_with_positions_reduced_potential})"
        vacuum_added_valence_energy = 0.0
    else:
        added_valence_energy = geometry_engine.forward_final_context_reduced_potential - geometry_engine.forward_atoms_with_positions_reduced_potential

    if not proposal.unique_old_atoms:
        assert geometry_engine.reverse_final_context_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.reverse_final_context_reduced_potential})"
        assert geometry_engine.reverse_atoms_with_positions_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.reverse_atoms_with_positions_reduced_potential})"
        subtracted_valence_energy = 0.0
    else:
        subtracted_valence_energy = geometry_engine.reverse_final_context_reduced_potential - geometry_engine.reverse_atoms_with_positions_reduced_potential

    zero_state_error, one_state_error = validate_endstate_energies(
        factory._topology_proposal,
        factory,
        added_valence_energy,
        subtracted_valence_energy,
        beta=1.0 / (kB * temperature),
        ENERGY_THRESHOLD=ENERGY_THRESHOLD,
        platform=openmm.Platform.getPlatformByName('Reference'))
    return factory
Exemplo n.º 31
0
def generate_solvated_hybrid_test_topology(current_mol_name="naphthalene",
                                           proposed_mol_name="benzene",
                                           current_mol_smiles=None,
                                           proposed_mol_smiles=None,
                                           vacuum=False,
                                           render_atom_mapping=False):
    """
    This function will generate a topology proposal, old positions, and new positions with a geometry proposal (either vacuum or solvated) given a set of input iupacs or smiles.
    The function will (by default) read the iupac names first.  If they are set to None, then it will attempt to read a set of current and new smiles.
    An atom mapping pdf will be generated if specified.
    Arguments
    ----------
    current_mol_name : str, optional
        name of the first molecule
    proposed_mol_name : str, optional
        name of the second molecule
    current_mol_smiles : str (default None)
        current mol smiles
    proposed_mol_smiles : str (default None)
        proposed mol smiles
    vacuum: bool (default False)
        whether to render a vacuum or solvated topology_proposal
    render_atom_mapping : bool (default False)
        whether to render the atom map of the current_mol_name and proposed_mol_name

    Returns
    -------
    topology_proposal : perses.rjmc.topology_proposal
        The topology proposal representing the transformation
    current_positions : np.array, unit-bearing
        The positions of the initial system
    new_positions : np.array, unit-bearing
        The positions of the new system
    """
    import simtk.openmm.app as app
    from openmoltools import forcefield_generators

    from openeye import oechem
    from openmoltools.openeye import iupac_to_oemol, generate_conformers, smiles_to_oemol
    from openmoltools import forcefield_generators
    import perses.utils.openeye as openeye
    from perses.utils.data import get_data_filename
    from perses.rjmc.topology_proposal import TopologyProposal, SystemGenerator, SmallMoleculeSetProposalEngine
    import simtk.unit as unit
    from perses.rjmc.geometry import FFAllAngleGeometryEngine

    if current_mol_name != None and proposed_mol_name != None:
        try:
            old_oemol, new_oemol = iupac_to_oemol(
                current_mol_name), iupac_to_oemol(proposed_mol_name)
            old_smiles = oechem.OECreateSmiString(
                old_oemol,
                oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)
            new_smiles = oechem.OECreateSmiString(
                new_oemol,
                oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)
        except:
            raise Exception(
                f"either {current_mol_name} or {proposed_mol_name} is not compatible with 'iupac_to_oemol' function!"
            )
    elif current_mol_smiles != None and proposed_mol_smiles != None:
        try:
            old_oemol, new_oemol = smiles_to_oemol(
                current_mol_smiles), smiles_to_oemol(proposed_mol_smiles)
            old_smiles = oechem.OECreateSmiString(
                old_oemol,
                oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)
            new_smiles = oechem.OECreateSmiString(
                new_oemol,
                oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)
        except:
            raise Exception(f"the variables are not compatible")
    else:
        raise Exception(
            f"either current_mol_name and proposed_mol_name must be specified as iupacs OR current_mol_smiles and proposed_mol_smiles must be specified as smiles strings."
        )

    old_oemol, old_system, old_positions, old_topology = openeye.createSystemFromSMILES(
        old_smiles, title="MOL")

    #correct the old positions
    old_positions = openeye.extractPositionsFromOEMol(old_oemol)
    old_positions = old_positions.in_units_of(unit.nanometers)

    new_oemol, new_system, new_positions, new_topology = openeye.createSystemFromSMILES(
        new_smiles, title="NEW")

    ffxml = forcefield_generators.generateForceFieldFromMolecules(
        [old_oemol, new_oemol])

    old_oemol.SetTitle('MOL')
    new_oemol.SetTitle('MOL')

    old_topology = forcefield_generators.generateTopologyFromOEMol(old_oemol)
    new_topology = forcefield_generators.generateTopologyFromOEMol(new_oemol)

    if not vacuum:
        nonbonded_method = app.PME
        barostat = openmm.MonteCarloBarostat(1.0 * unit.atmosphere,
                                             300.0 * unit.kelvin, 50)
    else:
        nonbonded_method = app.NoCutoff
        barostat = None

    gaff_xml_filename = get_data_filename("data/gaff.xml")
    system_generator = SystemGenerator(
        [gaff_xml_filename, 'amber99sbildn.xml', 'tip3p.xml'],
        barostat=barostat,
        forcefield_kwargs={
            'removeCMMotion': False,
            'nonbondedMethod': nonbonded_method,
            'constraints': app.HBonds,
            'hydrogenMass': 4.0 * unit.amu
        })
    system_generator._forcefield.loadFile(StringIO(ffxml))

    proposal_engine = SmallMoleculeSetProposalEngine([old_smiles, new_smiles],
                                                     system_generator,
                                                     residue_name='MOL')
    geometry_engine = FFAllAngleGeometryEngine(metadata=None,
                                               use_sterics=False,
                                               n_bond_divisions=1000,
                                               n_angle_divisions=180,
                                               n_torsion_divisions=360,
                                               verbose=True,
                                               storage=None,
                                               bond_softening_constant=1.0,
                                               angle_softening_constant=1.0,
                                               neglect_angles=False)

    if not vacuum:
        #now to solvate
        modeller = app.Modeller(old_topology, old_positions)
        hs = [
            atom for atom in modeller.topology.atoms()
            if atom.element.symbol in ['H']
            and atom.residue.name not in ['MOL', 'OLD', 'NEW']
        ]
        modeller.delete(hs)
        modeller.addHydrogens(forcefield=system_generator._forcefield)
        modeller.addSolvent(system_generator._forcefield,
                            model='tip3p',
                            padding=9.0 * unit.angstroms)
        solvated_topology = modeller.getTopology()
        solvated_positions = modeller.getPositions()
        solvated_positions = unit.quantity.Quantity(value=np.array([
            list(atom_pos) for atom_pos in
            solvated_positions.value_in_unit_system(unit.md_unit_system)
        ]),
                                                    unit=unit.nanometers)
        solvated_system = system_generator.build_system(solvated_topology)

        #now to create proposal
        top_proposal = proposal_engine.propose(
            current_system=solvated_system,
            current_topology=solvated_topology,
            current_mol=old_oemol,
            proposed_mol=new_oemol)
        new_positions, _ = geometry_engine.propose(top_proposal,
                                                   solvated_positions, beta)

        if render_atom_mapping:
            from perses.utils.smallmolecules import render_atom_mapping
            print(
                f"new_to_old: {proposal_engine.non_offset_new_to_old_atom_map}"
            )
            render_atom_mapping(f"{old_smiles}to{new_smiles}.png", old_oemol,
                                new_oemol,
                                proposal_engine.non_offset_new_to_old_atom_map)

        return top_proposal, solvated_positions, new_positions

    else:
        vacuum_system = system_generator.build_system(old_topology)
        top_proposal = proposal_engine.propose(current_system=vacuum_system,
                                               current_topology=old_topology,
                                               current_mol=old_oemol,
                                               proposed_mol=new_oemol)
        new_positions, _ = geometry_engine.propose(top_proposal, old_positions,
                                                   beta)
        if render_atom_mapping:
            from perses.utils.smallmolecules import render_atom_mapping
            print(f"new_to_old: {top_proposal._new_to_old_atom_map}")
            render_atom_mapping(f"{old_smiles}to{new_smiles}.png", old_oemol,
                                new_oemol, top_proposal._new_to_old_atom_map)
        return top_proposal, old_positions, new_positions
Exemplo n.º 32
0
    def __init__(self,
                 protein_filename,
                 mutation_chain_id,
                 mutation_residue_id,
                 proposed_residue,
                 phase='complex',
                 conduct_endstate_validation=True,
                 ligand_input=None,
                 ligand_index=0,
                 water_model='tip3p',
                 ionic_strength=0.15 * unit.molar,
                 forcefield_files=['amber14/protein.ff14SB.xml', 'amber14/tip3p.xml'],
                 barostat=openmm.MonteCarloBarostat(1.0 * unit.atmosphere, temperature, 50),
                 forcefield_kwargs={'removeCMMotion': False, 'ewaldErrorTolerance': 0.00025, 'constraints' : app.HBonds, 'hydrogenMass' : 4 * unit.amus},
                 periodic_forcefield_kwargs={'nonbondedMethod': app.PME},
                 nonperiodic_forcefield_kwargs=None,
                 small_molecule_forcefields='gaff-2.11',
                 complex_box_dimensions=None,
                 apo_box_dimensions=None,
                 flatten_torsions=False,
                 flatten_exceptions=False,
                 repartitioned_endstate=None,
                 **kwargs):
        """
        arguments
            protein_filename : str
                path to protein (to mutate); .pdb
            mutation_chain_id : str
                name of the chain to be mutated
            mutation_residue_id : str
                residue id to change
            proposed_residue : str
                three letter code of the residue to mutate to
            phase : str, default complex
                if phase == vacuum, then the complex will not be solvated with water; else, it will be solvated with tip3p
            conduct_endstate_validation : bool, default True
                whether to conduct an endstate validation of the HybridTopologyFactory. If using the RepartitionedHybridTopologyFactory,
                endstate validation cannot and will not be conducted.
            ligand_file : str, default None
                path to ligand of interest (i.e. small molecule or protein); .sdf or .pdb
            ligand_index : int, default 0
                which ligand to use
            water_model : str, default 'tip3p'
                solvent model to use for solvation
            ionic_strength : float * unit.molar, default 0.15 * unit.molar
                the total concentration of ions (both positive and negative) to add using Modeller.
                This does not include ions that are added to neutralize the system.
                Note that only monovalent ions are currently supported.
            forcefield_files : list of str, default ['amber14/protein.ff14SB.xml', 'amber14/tip3p.xml']
                forcefield files for proteins and solvent
            barostat : openmm.MonteCarloBarostat, default openmm.MonteCarloBarostat(1.0 * unit.atmosphere, 300 * unit.kelvin, 50)
                barostat to use
            forcefield_kwargs : dict, default {'removeCMMotion': False, 'ewaldErrorTolerance': 1e-4, 'constraints' : app.HBonds, 'hydrogenMass' : 4 * unit.amus}
                forcefield kwargs for system parametrization
            periodic_forcefield_kwargs : dict, default {'nonbondedMethod': app.PME}
                periodic forcefield kwargs for system parametrization
            nonperiodic_forcefield_kwargs : dict, default None
                non-periodic forcefield kwargs for system parametrization
            small_molecule_forcefields : str, default 'gaff-2.11'
                the forcefield string for small molecule parametrization
            complex_box_dimensions : Vec3, default None
                define box dimensions of complex phase;
                if None, padding is 1nm
            apo_box_dimensions :  Vec3, default None
                define box dimensions of apo phase phase;
                if None, padding is 1nm
            flatten_torsions : bool, default False
                in the htf, flatten torsions involving unique new atoms at lambda = 0 and unique old atoms are lambda = 1
            flatten_exceptions : bool, default False
                in the htf, flatten exceptions involving unique new atoms at lambda = 0 and unique old atoms at lambda = 1
            repartitioned_endstate : int, default None
                the endstate (0 or 1) at which to build the RepartitionedHybridTopologyFactory. By default, this is None,
                meaning a vanilla HybridTopologyFactory will be built.
        TODO : allow argument for spectator ligands besides the 'ligand_file'

        """

        # First thing to do is load the apo protein to mutate...
        protein_pdbfile = open(protein_filename, 'r')
        protein_pdb = app.PDBFile(protein_pdbfile)
        protein_pdbfile.close()
        protein_positions, protein_topology, protein_md_topology = protein_pdb.positions, protein_pdb.topology, md.Topology.from_openmm(protein_pdb.topology)
        protein_topology = protein_md_topology.to_openmm()
        protein_n_atoms = protein_md_topology.n_atoms

        # Load the ligand, if present
        molecules = []
        if ligand_input:
            if isinstance(ligand_input, str):
                if ligand_input.endswith('.sdf'): # small molecule
                        ligand_mol = createOEMolFromSDF(ligand_input, index=ligand_index)
                        molecules.append(Molecule.from_openeye(ligand_mol, allow_undefined_stereo=False))
                        ligand_positions, ligand_topology = extractPositionsFromOEMol(ligand_mol),  forcefield_generators.generateTopologyFromOEMol(ligand_mol)
                        ligand_md_topology = md.Topology.from_openmm(ligand_topology)
                        ligand_n_atoms = ligand_md_topology.n_atoms

                if ligand_input.endswith('pdb'): # protein
                    ligand_pdbfile = open(ligand_input, 'r')
                    ligand_pdb = app.PDBFile(ligand_pdbfile)
                    ligand_pdbfile.close()
                    ligand_positions, ligand_topology, ligand_md_topology = ligand_pdb.positions, ligand_pdb.topology, md.Topology.from_openmm(
                        ligand_pdb.topology)
                    ligand_n_atoms = ligand_md_topology.n_atoms

            elif isinstance(ligand_input, oechem.OEMol): # oemol object
                molecules.append(Molecule.from_openeye(ligand_input, allow_undefined_stereo=False))
                ligand_positions, ligand_topology = extractPositionsFromOEMol(ligand_input),  forcefield_generators.generateTopologyFromOEMol(ligand_input)
                ligand_md_topology = md.Topology.from_openmm(ligand_topology)
                ligand_n_atoms = ligand_md_topology.n_atoms

            else:
                _logger.warning(f'ligand filetype not recognised. Please provide a path to a .pdb or .sdf file')
                return

            # Now create a complex
            complex_md_topology = protein_md_topology.join(ligand_md_topology)
            complex_topology = complex_md_topology.to_openmm()
            complex_positions = unit.Quantity(np.zeros([protein_n_atoms + ligand_n_atoms, 3]), unit=unit.nanometers)
            complex_positions[:protein_n_atoms, :] = protein_positions
            complex_positions[protein_n_atoms:, :] = ligand_positions

        # Now for a system_generator
        self.system_generator = SystemGenerator(forcefields=forcefield_files,
                                                barostat=barostat,
                                                forcefield_kwargs=forcefield_kwargs,
                                                periodic_forcefield_kwargs=periodic_forcefield_kwargs,
                                                nonperiodic_forcefield_kwargs=nonperiodic_forcefield_kwargs,
                                                small_molecule_forcefield=small_molecule_forcefields,
                                                molecules=molecules,
                                                cache=None)

        # Solvate apo and complex...
        apo_input = list(self._solvate(protein_topology, protein_positions, water_model, phase, ionic_strength, apo_box_dimensions))
        inputs = [apo_input]
        if ligand_input:
            inputs.append(self._solvate(complex_topology, complex_positions, water_model, phase, ionic_strength, complex_box_dimensions))

        geometry_engine = FFAllAngleGeometryEngine(metadata=None,
                                                use_sterics=False,
                                                n_bond_divisions=100,
                                                n_angle_divisions=180,
                                                n_torsion_divisions=360,
                                                verbose=True,
                                                storage=None,
                                                bond_softening_constant=1.0,
                                                angle_softening_constant=1.0,
                                                neglect_angles = False,
                                                use_14_nonbondeds = True)


        # Run pipeline...
        htfs = []
        for (top, pos, sys) in inputs:
            point_mutation_engine = PointMutationEngine(wildtype_topology=top,
                                                                 system_generator=self.system_generator,
                                                                 chain_id=mutation_chain_id, # Denote the chain id allowed to mutate (it's always a string variable)
                                                                 max_point_mutants=1,
                                                                 residues_allowed_to_mutate=[mutation_residue_id], # The residue ids allowed to mutate
                                                                 allowed_mutations=[(mutation_residue_id, proposed_residue)], # The residue ids allowed to mutate with the three-letter code allowed to change
                                                                 aggregate=True) # Always allow aggregation

            topology_proposal = point_mutation_engine.propose(sys, top)

            # Only validate energy bookkeeping if the WT and proposed residues do not involve rings
            old_res = [res for res in top.residues() if res.id == mutation_residue_id][0]
            validate_bool = False if old_res.name in ring_amino_acids or proposed_residue in ring_amino_acids else True
            new_positions, logp_proposal = geometry_engine.propose(topology_proposal, pos, beta,
                                                                   validate_energy_bookkeeping=validate_bool)
            logp_reverse = geometry_engine.logp_reverse(topology_proposal, new_positions, pos, beta,
                                                        validate_energy_bookkeeping=validate_bool)

            if repartitioned_endstate is None:
                factory = HybridTopologyFactory
            elif repartitioned_endstate in [0, 1]:
                factory = RepartitionedHybridTopologyFactory

            forward_htf = factory(topology_proposal=topology_proposal,
                                  current_positions=pos,
                                  new_positions=new_positions,
                                  use_dispersion_correction=False,
                                  functions=None,
                                  softcore_alpha=None,
                                  bond_softening_constant=1.0,
                                  angle_softening_constant=1.0,
                                  soften_only_new=False,
                                  neglected_new_angle_terms=[],
                                  neglected_old_angle_terms=[],
                                  softcore_LJ_v2=True,
                                  softcore_electrostatics=True,
                                  softcore_LJ_v2_alpha=0.85,
                                  softcore_electrostatics_alpha=0.3,
                                  softcore_sigma_Q=1.0,
                                  interpolate_old_and_new_14s=flatten_exceptions,
                                  omitted_terms=None,
                                  endstate=repartitioned_endstate,
                                  flatten_torsions=flatten_torsions)

            if not topology_proposal.unique_new_atoms:
                assert geometry_engine.forward_final_context_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.forward_final_context_reduced_potential})"
                assert geometry_engine.forward_atoms_with_positions_reduced_potential == None, f"There are no unique new atoms but the geometry_engine's forward atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.forward_atoms_with_positions_reduced_potential})"
            else:
                added_valence_energy = geometry_engine.forward_final_context_reduced_potential - geometry_engine.forward_atoms_with_positions_reduced_potential

            if not topology_proposal.unique_old_atoms:
                assert geometry_engine.reverse_final_context_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's final context reduced potential is not None (i.e. {self._geometry_engine.reverse_final_context_reduced_potential})"
                assert geometry_engine.reverse_atoms_with_positions_reduced_potential == None, f"There are no unique old atoms but the geometry_engine's atoms-with-positions-reduced-potential in not None (i.e. { self._geometry_engine.reverse_atoms_with_positions_reduced_potential})"
                subtracted_valence_energy = 0.0
            else:
                subtracted_valence_energy = geometry_engine.reverse_final_context_reduced_potential - geometry_engine.reverse_atoms_with_positions_reduced_potential


            if conduct_endstate_validation and repartitioned_endstate is None:
                zero_state_error, one_state_error = validate_endstate_energies(forward_htf._topology_proposal, forward_htf, added_valence_energy, subtracted_valence_energy, beta=beta, ENERGY_THRESHOLD=ENERGY_THRESHOLD)
                if zero_state_error > ENERGY_THRESHOLD:
                    _logger.warning(f"Reduced potential difference of the nonalchemical and alchemical Lambda = 0 state is above the threshold ({ENERGY_THRESHOLD}): {zero_state_error}")
                if one_state_error > ENERGY_THRESHOLD:
                    _logger.warning(f"Reduced potential difference of the nonalchemical and alchemical Lambda = 1 state is above the threshold ({ENERGY_THRESHOLD}): {one_state_error}")
            else:
                pass

            htfs.append(forward_htf)

        self.apo_htf = htfs[0]
        self.complex_htf = htfs[1] if ligand_input else None
Exemplo n.º 33
0
def generate_complex_topologies_and_positions(ligand_filename, protein_pdb_filename):
    """
    Generate the topologies and positions for complex phase simulations, given an input ligand file (in supported openeye
    format) and protein pdb file. Note that the input ligand file should have coordinates placing the ligand in the binding
    site.

    Parameters
    ----------
    ligand_filename : str
        Name of the file containing ligands
    protein_pdb_filename : str
        Name of the protein pdb file

    Returns
    -------
    complex_topologies_dict : dict of smiles: md.topology
        Dictionary of topologies for various complex systems
    complex_positions_dict : dict of smiles:  [n, 3] array of Quantity
        Positions for corresponding complexes
    """
    ifs = oechem.oemolistream()
    ifs.open(ligand_filename)

    # get the list of molecules
    mol_list = [oechem.OEMol(mol) for mol in ifs.GetOEMols()]

    for idx, mol in enumerate(mol_list):
        mol.SetTitle("MOL{}".format(idx))
        oechem.OETriposAtomNames(mol)

    mol_dict = {oechem.OEMolToSmiles(mol) : mol for mol in mol_list}

    ligand_topology_dict = {smiles : forcefield_generators.generateTopologyFromOEMol(mol) for smiles, mol in mol_dict.items()}


    protein_pdbfile = open(protein_pdb_filename, 'r')
    pdb_file = app.PDBFile(protein_pdbfile)
    protein_pdbfile.close()
    receptor_positions = pdb_file.positions
    receptor_topology = pdb_file.topology
    receptor_md_topology = md.Topology.from_openmm(receptor_topology)

    n_receptor_atoms = receptor_md_topology.n_atoms

    complex_topologies = {}
    complex_positions_dict = {}

    for smiles, ligand_topology in ligand_topology_dict.items():
        ligand_md_topology = md.Topology.from_openmm(ligand_topology)

        n_complex_atoms = ligand_md_topology.n_atoms + n_receptor_atoms
        copy_receptor_md_topology = copy.deepcopy(receptor_md_topology)

        complex_positions = unit.Quantity(np.zeros([n_complex_atoms, 3]), unit=unit.nanometers)

        complex_topology = copy_receptor_md_topology.join(ligand_md_topology)

        complex_topologies[smiles] = complex_topology

        ligand_positions = extractPositionsFromOEMOL(mol_dict[smiles])

        complex_positions[:n_receptor_atoms, :] = receptor_positions
        complex_positions[n_receptor_atoms:, :] = ligand_positions

        complex_positions_dict[smiles] = complex_positions

    return complex_topologies, complex_positions_dict