Beispiel #1
0
def test_simple_heterocycle_mapping(iupac_pairs=[('benzene', 'pyridine')]):
    """
    Test the ability to map conjugated heterocycles (that preserves all rings).  Will assert that the number of ring members in both molecules is the same.
    """
    # TODO: generalize this to test for ring breakage and closure.
    from openmoltools.openeye import iupac_to_oemol
    from openeye import oechem
    from perses.rjmc.topology_proposal import AtomMapper

    for iupac_pair in iupac_pairs:
        old_oemol, new_oemol = iupac_to_oemol(iupac_pair[0]), iupac_to_oemol(
            iupac_pair[1])
        new_to_old_map = AtomMapper._get_mol_atom_map(
            old_oemol, new_oemol, allow_ring_breaking=False)

        #assert that the number of ring members is consistent in the mapping...
        num_hetero_maps = 0
        for new_index, old_index in new_to_old_map.items():
            old_atom, new_atom = old_oemol.GetAtom(
                oechem.OEHasAtomIdx(old_index)), new_oemol.GetAtom(
                    oechem.OEHasAtomIdx(new_index))
            if old_atom.IsInRing() and new_atom.IsInRing():
                if old_atom.GetAtomicNum() != new_atom.GetAtomicNum():
                    num_hetero_maps += 1

        assert num_hetero_maps > 0, f"there are no differences in atomic number mappings in {iupac_pair}"
Beispiel #2
0
def test_ring_breaking_detection():
    """
    Test the detection of ring-breaking transformations.

    """
    from perses.rjmc.topology_proposal import SmallMoleculeSetProposalEngine, AtomMapper
    from openmoltools.openeye import iupac_to_oemol, generate_conformers
    molecule1 = iupac_to_oemol("naphthalene")
    molecule2 = iupac_to_oemol("benzene")
    molecule1 = generate_conformers(molecule1, max_confs=1)
    molecule2 = generate_conformers(molecule2, max_confs=1)

    # Allow ring breaking
    new_to_old_atom_map = AtomMapper._get_mol_atom_map(
        molecule1, molecule2, allow_ring_breaking=True)
    if not len(new_to_old_atom_map) > 0:
        filename = 'mapping-error.png'
        #render_atom_mapping(filename, molecule1, molecule2, new_to_old_atom_map)
        msg = 'Napthalene -> benzene transformation with allow_ring_breaking=True is not returning a valid mapping\n'
        msg += 'Wrote atom mapping to %s for inspection; please check this.' % filename
        msg += str(new_to_old_atom_map)
        raise Exception(msg)

    new_to_old_atom_map = AtomMapper._get_mol_atom_map(
        molecule1, molecule2, allow_ring_breaking=False)
    if new_to_old_atom_map is not None:  # atom mapper should not retain _any_ atoms in default mode
        filename = 'mapping-error.png'
        #render_atom_mapping(filename, molecule1, molecule2, new_to_old_atom_map)
        msg = 'Napthalene -> benzene transformation with allow_ring_breaking=False is erroneously allowing ring breaking\n'
        msg += 'Wrote atom mapping to %s for inspection; please check this.' % filename
        msg += str(new_to_old_atom_map)
        raise Exception(msg)
def compare_energies(mol_name="naphthalene", ref_mol_name="benzene"):
    """
    Make an atom map where the molecule at either lambda endpoint is identical, and check that the energies are also the same.
    """
    from openmmtools import alchemy, states
    from perses.rjmc.topology_proposal import SmallMoleculeSetProposalEngine, TopologyProposal
    from perses.annihilation.relative import HybridTopologyFactory
    import simtk.openmm as openmm

    from perses.utils.openeye import createSystemFromIUPAC
    from openmoltools.openeye import iupac_to_oemol, generate_conformers

    mol = iupac_to_oemol(mol_name)
    mol = generate_conformers(mol, max_confs=1)
    m, system, positions, topology = createSystemFromIUPAC(mol_name)

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

    #map one of the rings
    atom_map = SmallMoleculeSetProposalEngine._get_mol_atom_map(mol, refmol)

    #now use the mapped atoms to generate a new and old system with identical atoms mapped. This will result in the
    #same molecule with the same positions for lambda=0 and 1, and ensures a contiguous atom map
    effective_atom_map = {value: value for value in atom_map.values()}

    #make a topology proposal with the appropriate data:
    top_proposal = TopologyProposal(new_topology=topology,
                                    new_system=system,
                                    old_topology=topology,
                                    old_system=system,
                                    new_to_old_atom_map=effective_atom_map,
                                    new_chemical_state_key="n1",
                                    old_chemical_state_key='n2')

    factory = HybridTopologyFactory(top_proposal, positions, positions)

    alchemical_system = factory.hybrid_system
    alchemical_positions = factory.hybrid_positions

    platform = openmm.Platform.getPlatformByName("Reference")

    _, _, alch_zero_state, alch_one_state = utils.generate_endpoint_thermodynamic_states(
        alchemical_system, top_proposal)

    rp_list = []
    for state in [alch_zero_state, alch_one_state]:
        integrator = openmm.VerletIntegrator(1)
        context = state.create_context(integrator, platform)
        samplerstate = states.SamplerState(
            positions=alchemical_positions,
            box_vectors=alchemical_system.getDefaultPeriodicBoxVectors())
        samplerstate.apply_to_context(context)
        rp = state.reduced_potential(context)
        rp_list.append(rp)
        del context, integrator

    assert abs(rp_list[0] - rp_list[1]) < 1e-6
Beispiel #4
0
def create_molecule(iupac_name):
    """
    Create an OEMol molecule from an IUPAC name.

    Parameters
    ----------
    iupac_name : str
        The IUPAC name of the molecule to be created.

    Returns
    -------
    molecule : openeye.oechem.OEMol
        A molecule with AM1-BCC charges.

    """

    molecule = openeye.iupac_to_oemol(iupac_name)

    # Assign AM1-BCC charges using canonical scheme.
    # TODO: Replace wit updated gaff2xml scheme.
    molecule = assign_am1bcc_charges(molecule)

    # Assign conformations.
    from openeye import oeomega
    omega = oeomega.OEOmega()
    omega.SetMaxConfs(1)
    omega(molecule)

    return molecule
def run_oemol_test_suite(iupac='ethane'):
   """
   Runs all of the oemol related tests for perses.utils.openeye

   Parameters
   ---------
   iupac : str, default 'ethane'

   """
   from openmoltools.openeye import iupac_to_oemol
   import copy
   import numpy as np
   import simtk.unit as unit
   from openeye import oechem

   oemol = iupac_to_oemol(iupac)
   positions = test_extractPositionsFromOEMol(oemol)

   # shifting all of the positions by 1. A
   new_positions = np.zeros(np.shape(positions))
   for atom in range(oemol.NumAtoms()):
       new_positions[atom] = copy.deepcopy(positions[atom]) + [1., 1., 1.]*unit.angstrom
   new_positions *= unit.angstrom

   molecule = test_giveOpenmmPositionsToOEMol(new_positions,oemol)

   smiles = oechem.OECreateSmiString(molecule,oechem.OESMILESFlag_DEFAULT | oechem.OESMILESFlag_Hydrogens)

   smiles_oemol = smiles_to_oemol(smiles)

   # check that the two systems have the same numbers of atoms
   assert (oemol.NumAtoms() == smiles_oemol.NumAtoms()), "Discrepancy between molecule generated from IUPAC and SMILES"
Beispiel #6
0
def create_molecule(iupac_name):
    """
    Create an OEMol molecule from an IUPAC name.

    Parameters
    ----------
    iupac_name : str
        The IUPAC name of the molecule to be created.

    Returns
    -------
    molecule : openeye.oechem.OEMol
        A molecule with AM1-BCC charges.

    """

    molecule = openeye.iupac_to_oemol(iupac_name)

    # Assign AM1-BCC charges using canonical scheme.
    # TODO: Replace wit updated gaff2xml scheme.
    molecule = assign_am1bcc_charges(molecule)

    # Assign conformations.
    from openeye import oeomega
    omega = oeomega.OEOmega()
    omega.SetMaxConfs(1)
    omega(molecule)

    return molecule
Beispiel #7
0
def createSystemFromIUPAC(iupac_name):
    """
    Create an openmm system out of an oemol

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

    Returns
    -------
    molecule : openeye.oechem.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
    # TODO write our own of this function so we can be sure of the oe flags that are being used
    molecule = iupac_to_oemol(iupac_name)

    molecule = generate_conformers(molecule, max_confs=1)

    # generate openmm system, positions and topology
    system, positions, topology = OEMol_to_omm_ff(molecule)

    return (molecule, system, positions, topology)
Beispiel #8
0
def parameterize_molecule(molecule, implicitSolvent=app.OBC1, constraints=None, cleanup=True, verbose=False):
    """
    Parameterize the specified molecule for AMBER.

    Parameters
    ----------
    molecule : openeye.oechem.OEMol
        The molecule to be parameterized.
    implicitSolvent : default=app.OBC1
        The implicit solvent model to use; one of [None, HCT, OBC1, OBC2, GBn, GBn2]
    constraints : default=None
        Constraints to use; one of [None, HBonds, AllBonds, HAngles]
    cleanup : bool, optional, default=False
        If True, work done in a temporary working directory will be deleted.

    Returns
    -------
    system : simtk.openmm.System
        The OpenMM System of the molecule.
    topology : simtk.openmm.app.Topology
        The OpenMM topology of the molecule.
    positions :
        The positions of the molecule.
    gaff_molecule : oechem.OEMol
        The OEMol molecule with GAFF atom and bond types.

    """
    # Create molecule and geometry.
    molecule = openeye.iupac_to_oemol(iupac_name)
    # Create a a temporary directory.
    working_directory = tempfile.mkdtemp()
    old_directory = os.getcwd()
    os.chdir(working_directory)
    # Parameterize molecule for AMBER (currently using old machinery for convenience)
    # TODO: Replace this with gaff2xml stuff
    amber_prmtop_filename = 'molecule.prmtop'
    amber_inpcrd_filename = 'molecule.inpcrd'
    amber_off_filename = 'molecule.off'
    oldmmtools.parameterizeForAmber(molecule, amber_prmtop_filename, amber_inpcrd_filename, charge_model=None, offfile=amber_off_filename)
    # Read in the molecule with GAFF atom and bond types
    print "Overwriting OEMol with GAFF atom and bond types..."
    gaff_molecule = oldmmtools.loadGAFFMolecule(molecule, amber_off_filename)

    # Load positions.
    inpcrd = app.AmberInpcrdFile(amber_inpcrd_filename)
    positions = inpcrd.getPositions()

    # Load system (with GB parameters).
    prmtop = app.AmberPrmtopFile(amber_prmtop_filename)
    system = prmtop.createSystem(implicitSolvent=implicitSolvent, constraints=constraints)

    # Clean up temporary files.
    os.chdir(old_directory)
    if cleanup:
        commands.getoutput('rm -r %s' % working_directory)
    else:
        print "Work done in %s..." % working_directory

    return [system, topology, positions, gaff_molecule]
Beispiel #9
0
def create_molecule(iupac_name):
    molecule = openeye.iupac_to_oemol(iupac_name)
    molecule = openeye.get_charges(molecule, max_confs=1)
    #import openeye.oeomega as om
    #omega = om.OEOmega()
    #omega.SetMaxConfs(1)
    #omega(molecule)
    return molecule
Beispiel #10
0
def parameterize_molecule(molecule,
                          implicitSolvent=app.OBC1,
                          constraints=None,
                          cleanup=True,
                          verbose=False):
    """
    Parameterize the specified molecule for AMBER.

    Parameters
    ----------
    molecule : openeye.oechem.OEMol
        The molecule to be parameterized.
    implicitSolvent : default=app.OBC1
        The implicit solvent model to use; one of [None, HCT, OBC1, OBC2, GBn, GBn2]
    constraints : default=None
        Constraints to use; one of [None, HBonds, AllBonds, HAngles]
    cleanup : bool, optional, default=False
        If True, work done in a temporary working directory will be deleted.

    Returns
    -------
    system : simtk.openmm.System
        The OpenMM System of the molecule.
    topology : simtk.openmm.app.Topology
        The OpenMM topology of the molecule.
    positions :
        The positions of the molecule.
    gaff_molecule : oechem.OEMol
        The OEMol molecule with GAFF atom and bond types.

    """
    # Create molecule and geometry.
    molecule = openeye.iupac_to_oemol(iupac_name)
    # Create a a temporary directory.
    working_directory = tempfile.mkdtemp()
    old_directory = os.getcwd()
    os.chdir(working_directory)
    # Parameterize molecule for AMBER (currently using old machinery for convenience)
    # TODO: Replace this with gaff2xml stuff
    amber_prmtop_filename = 'molecule.prmtop'
    amber_inpcrd_filename = 'molecule.inpcrd'
    amber_off_filename = 'molecule.off'
    oldmmtools.parameterizeForAmber(molecule,
                                    amber_prmtop_filename,
                                    amber_inpcrd_filename,
                                    charge_model=None,
                                    offfile=amber_off_filename)
    # Read in the molecule with GAFF atom and bond types
    print "Overwriting OEMol with GAFF atom and bond types..."
    gaff_molecule = oldmmtools.loadGAFFMolecule(molecule, amber_off_filename)

    # Load positions.
    inpcrd = app.AmberInpcrdFile(amber_inpcrd_filename)
    positions = inpcrd.getPositions()

    # Load system (with GB parameters).
    prmtop = app.AmberPrmtopFile(amber_prmtop_filename)
    system = prmtop.createSystem(implicitSolvent=implicitSolvent,
                                 constraints=constraints)

    # Clean up temporary files.
    os.chdir(old_directory)
    if cleanup:
        commands.getoutput('rm -r %s' % working_directory)
    else:
        print "Work done in %s..." % working_directory

    return [system, topology, positions, gaff_molecule]
Beispiel #11
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