Esempio n. 1
0
def test_parameter_round_trip(method, tmpdir):
    """
    Check we can parametrise a molecule then write out the same parameters.
    """
    with tmpdir.as_cwd():
        mol = Ligand.from_file(get_data("acetone.sdf"))
        method(mol)
        # write out params
        mol.write_parameters(name="test")

        # make a second mol
        mol2 = Ligand.from_file(get_data("acetone.sdf"))
        XML(mol2, "test.xml")

        assert mol.AtomTypes == mol2.AtomTypes
        for bond in mol.HarmonicBondForce.keys():
            assert mol.HarmonicBondForce[bond] == pytest.approx(
                mol2.HarmonicBondForce[bond], abs=1e-6
            )
        for angle in mol.HarmonicAngleForce.keys():
            assert mol.HarmonicAngleForce[angle] == pytest.approx(
                mol2.HarmonicAngleForce[angle], abs=1e-6
            )
        for atom in range(mol.n_atoms):
            assert (
                pytest.approx(mol.NonbondedForce[atom], abs=1e-6)
                == mol2.NonbondedForce[atom]
            )
        for dihedral, terms in mol.PeriodicTorsionForce.items():
            try:
                other_dih = mol2.PeriodicTorsionForce[dihedral]
            except KeyError:
                other_dih = mol2.PeriodicTorsionForce[tuple(reversed(dihedral))]
            assert np.allclose(terms[:4], other_dih[:4])
Esempio n. 2
0
    def handle_bulk(self):
        """
        Getting and setting configs for bulk runs is a little different, requiring this method.
        The configs are taken from the .csv, then the .ini, then the terminal.
        This is repeated for each molecule in the bulk run, then Execute is called.

        Configs cannot be changed between molecule analyses as config data is
        only loaded once at the start; -restart is required for that.
        """

        csv_file = self.args.bulk_run
        # mol_data_from_csv handles defaults if no argument is given
        bulk_data = mol_data_from_csv(csv_file)

        names = list(bulk_data)

        home = os.getcwd()

        for name in names:
            printf(f'Analysing: {name}\n')

            # Get pdb from smiles or name if no smiles is given
            if bulk_data[name]['smiles'] is not None:
                smiles_string = bulk_data[name]['smiles']
                rdkit = RDKit()
                self.file = rdkit.smiles_to_pdb(smiles_string, name)

            else:
                self.file = f'{name}.pdb'

            # Initialise molecule, ready to add configs to it
            self.molecule = Ligand(self.file)

            # Read each row in bulk data and set it to the molecule object
            for key, val in bulk_data[name].items():
                setattr(self.molecule, key, val)

            setattr(self.molecule, 'skip', [])

            # Using the config file from the .csv, gather the .ini file configs
            file_configs = Configure.load_config(self.molecule.config)
            for key, val in file_configs.items():
                setattr(self.molecule, key, val)

            # Handle configs which are changed by terminal commands
            for key, val in vars(self.args).items():
                if val is not None:
                    setattr(self.molecule, key, val)

            # Now that all configs are stored correctly: execute.
            Execute(self.molecule)

            os.chdir(home)

        sys.exit(
            'Bulk analysis complete.\nUse QUBEKit -progress to view the completion progress of your molecules'
        )
Esempio n. 3
0
    def setUpClass(cls):

        cls.files_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'files')
        os.mkdir('temp')
        os.chdir('temp')
        copy(os.path.join(cls.files_folder, 'acetone.pdb'), 'acetone.pdb')
        cls.molecule_pdb = Ligand('acetone.pdb')
        copy(os.path.join(cls.files_folder, 'acetone.mol2'), 'acetone.mol2')
        cls.molecule_mol2 = Ligand('acetone.mol2')
        os.chdir('../')
Esempio n. 4
0
    def __init__(self):
        # First make sure the config folder has been made missing for conda and pip
        home = str(Path.home())
        config_folder = f'{home}/QUBEKit_configs/'
        if not os.path.exists(config_folder):
            os.makedirs(config_folder)
            print(f'Making config folder at: {home}')

        self.args = self.parse_commands()

        # If it's a bulk run, handle it separately
        # TODO Add .sdf as possible bulk_run, not just .csv
        if self.args.bulk_run:
            self.handle_bulk()

        if self.args.restart is not None:
            # Find the pickled checkpoint file and load it as the molecule
            try:
                self.molecule = unpickle()[self.args.restart]
            except FileNotFoundError:
                raise FileNotFoundError('No checkpoint file found!')
        else:
            if self.args.smiles:
                self.file = RDKit().smiles_to_pdb(*self.args.smiles)
            else:
                self.file = self.args.input

            # Initialise molecule
            self.molecule = Ligand(self.file)

        # Find which config file is being used
        self.molecule.config = self.args.config_file

        # Handle configs which are in a file
        file_configs = Configure.load_config(self.molecule.config)
        for name, val in file_configs.items():
            setattr(self.molecule, name, val)

        # Although these may be None always, they need to be explicitly set anyway.
        setattr(self.molecule, 'restart', None)
        setattr(self.molecule, 'end', None)
        setattr(self.molecule, 'skip', None)

        # Handle configs which are changed by terminal commands
        for name, val in vars(self.args).items():
            if val is not None:
                setattr(self.molecule, name, val)

        # If restarting put the molecule back into the checkpoint file with the new configs
        if self.args.restart is not None:
            self.molecule.pickle(state=self.args.restart)
        # Now that all configs are stored correctly: execute.
        Execute(self.molecule)
Esempio n. 5
0
    def setUpClass(cls):
        """
        Write the big string in test_structures to a file to be used for testing.
        Cannot use actual files as pathing causes issues.
        """
        with open('acetone.pdb', 'w+') as pdb_test_file:
            pdb_test_file.write(acetone)

        with open('acetone.mol2', 'w+') as mol2_test_file:
            mol2_test_file.write(acetone_mol2)

        cls.molecule_pdb = Ligand('acetone.pdb')
        cls.molecule_mol2 = Ligand('acetone.mol2')
Esempio n. 6
0
def test_no_rotatable_bonds():
    """
    If there are no dihedrals in the molecule make sure we return None.
    """
    mol = Ligand.from_file(file_name=get_data("water.pdb"))
    assert mol.rotatable_bonds is None
    assert mol.n_rotatable_bonds == 0
Esempio n. 7
0
def test_no_impropers():
    """
    Make sure we return None when no impropers are found in the molecule.
    """
    mol = Ligand.from_file(file_name=get_data("water.pdb"))
    assert mol.improper_torsions is None
    assert mol.n_improper_torsions == 0
Esempio n. 8
0
def test_no_dihedrals():
    """
    Make sure we return None when no dihedrals are found in the molecule.
    """
    mol = Ligand.from_file(file_name=get_data("water.pdb"))
    assert mol.dihedrals is None
    assert mol.n_dihedrals == 0
Esempio n. 9
0
    def test_smiles(self):

        # create a new molecule from a smiles string
        molecule_smiles = Ligand('CCC', 'ethane')

        # check the internal structures
        angles = {
            (1, 0, 3): 113.51815048217622,
            (1, 0, 4): 108.585923222101,
            (1, 0, 5): 106.72547221240829,
            (3, 0, 4): 108.67471750338844,
            (3, 0, 5): 109.86966536530876,
            (4, 0, 5): 109.3960638804494,
            (0, 1, 2): 112.47821537702777,
            (0, 1, 6): 106.25702918976113,
            (0, 1, 7): 113.72590402122567,
            (2, 1, 6): 106.3390387838715,
            (2, 1, 7): 111.69729882714941,
            (6, 1, 7): 105.65819247884409,
            (1, 2, 8): 108.59874810898711,
            (1, 2, 9): 112.19545440609062,
            (1, 2, 10): 111.67294842834627,
            (8, 2, 9): 111.33448705926884,
            (8, 2, 10): 107.47750840394838,
            (9, 2, 10): 105.46240504563437
        }

        self.assertEqual(angles, molecule_smiles.angle_values)
Esempio n. 10
0
def test_to_rdkit(molecule):
    """
    Make sure we can convert to rdkit.
    We test on bace which has a chiral center and 12-dichloroethene which has a stereo bond.
    """
    from rdkit import Chem

    mol = Ligand.from_file(file_name=get_data(molecule))
    rd_mol = mol.to_rdkit()
    # make sure the atom and bond stereo match
    for atom in rd_mol.GetAtoms():
        qb_atom = mol.atoms[atom.GetIdx()]
        assert atom.GetIsAromatic() is qb_atom.aromatic
        if qb_atom.stereochemistry is not None:
            if qb_atom.stereochemistry == "S":
                assert atom.GetChiralTag() == Chem.CHI_TETRAHEDRAL_CCW
            else:
                assert atom.GetChiralTag() == Chem.CHI_TETRAHEDRAL_CW
    for bond in rd_mol.GetBonds():
        qb_bond = mol.bonds[bond.GetIdx()]
        assert qb_bond.aromatic is bond.GetIsAromatic()
        assert qb_bond.bond_order == bond.GetBondTypeAsDouble()
        if qb_bond.stereochemistry is not None:
            if qb_bond.stereochemistry == "E":
                assert bond.GetStereo() == Chem.BondStereo.STEREOE
            else:
                assert bond.GetStereo() == Chem.BondStereo.STEREOZ
Esempio n. 11
0
    def test_smiles(self):
        """Ensure molecule is correctly generated from a smiles string."""

        molecule_smiles = Ligand('CCC', 'propane')

        angles = {
            (1, 0, 3): 113.51815048217622,
            (1, 0, 4): 108.585923222101,
            (1, 0, 5): 106.72547221240829,
            (3, 0, 4): 108.67471750338844,
            (3, 0, 5): 109.86966536530876,
            (4, 0, 5): 109.3960638804494,
            (0, 1, 2): 112.47821537702777,
            (0, 1, 6): 106.25702918976113,
            (0, 1, 7): 113.72590402122567,
            (2, 1, 6): 106.3390387838715,
            (2, 1, 7): 111.69729882714941,
            (6, 1, 7): 105.65819247884409,
            (1, 2, 8): 108.59874810898711,
            (1, 2, 9): 112.19545440609062,
            (1, 2, 10): 111.67294842834627,
            (8, 2, 9): 111.33448705926884,
            (8, 2, 10): 107.47750840394838,
            (9, 2, 10): 105.46240504563437
        }

        # loop over the angles and make sure they are almost equal to 7 d.p
        # the dictionaries will always be made in the same order unless the RDKit ordering changes.
        for angle, value in angles.items():
            self.assertAlmostEqual(molecule_smiles.angle_values[angle], value)
Esempio n. 12
0
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when Torsion maker is called."""

                # TODO Should this be here?

                # load in the ligand molecule
                mol = Ligand(values)

                # Prompt the user for the scan order
                scanner = TorsionScan(mol)
                scanner.find_scan_order()

                # Write out the scan file
                with open('QUBE_torsions.txt', 'w+') as qube:
                    qube.write(
                        '# dihedral definition by atom indices starting from 1\n#  i      j      k      l\n'
                    )
                    for scan in mol.scan_order:
                        scan_di = mol.dihedrals[scan][0]
                        qube.write(
                            f'  {scan_di[0]:2}     {scan_di[1]:2}     {scan_di[2]:2}     {scan_di[3]:2}\n'
                        )
                printf('QUBE_torsions.txt made.')

                sys.exit()
Esempio n. 13
0
    def setUp(self):
        """
        Set up the ligand testing class, make temp folder and copy the pdb and mol2 over
        """

        self.home = os.getcwd()
        self.test_folder = os.path.join(os.path.dirname(__file__), 'files')

        # Make the temp folder and move there with the required files
        with tempfile.TemporaryDirectory() as temp:
            os.chdir(temp)
            copy(os.path.join(self.test_folder, 'acetone.pdb'), 'acetone.pdb')
            self.molecule_pdb = Ligand('acetone.pdb')
            copy(os.path.join(self.test_folder, 'acetone.mol2'),
                 'acetone.mol2')
            self.molecule_mol2 = Ligand('acetone.mol2')
Esempio n. 14
0
def test_xml_sites_roundtrip(tmpdir):
    """
    If we load in an xml with sites make sure we can write it back out.
    """

    with tmpdir.as_cwd():
        mol = Ligand.from_file(get_data("pyridine.pdb"))
        XML(mol, input_file=get_data("pyridine.xml"))

        mol.write_parameters(name="test")

        mol2 = Ligand.from_file(get_data("pyridine.pdb"))
        XML(mol2, input_file="test.xml")

        assert mol.AtomTypes == mol2.AtomTypes
        for bond in mol.HarmonicBondForce.keys():
            assert (
                pytest.approx(mol.HarmonicBondForce[bond], abs=1e-6)
                == mol2.HarmonicBondForce[bond]
            )
        for angle in mol.HarmonicAngleForce.keys():
            assert (
                pytest.approx(mol.HarmonicAngleForce[angle], abs=1e-6)
                == mol2.HarmonicAngleForce[angle]
            )
        for atom in range(mol.n_atoms):
            assert (
                pytest.approx(mol.NonbondedForce[atom], abs=1e-6)
                == mol2.NonbondedForce[atom]
            )
        # make sure the virtual site was round tripped
        mol_site = mol.extra_sites[0]
        mol2_site = mol2.extra_sites[0]
        assert mol_site.charge == mol2_site.charge
        assert mol_site.parent_index == mol2_site.parent_index
        assert mol_site.closest_a_index == mol2_site.closest_a_index
        assert mol_site.closest_b_index == mol2_site.closest_b_index
        assert mol_site.p1 == mol2_site.p1
        assert mol_site.p2 == mol2_site.p2
        assert mol_site.p3 == mol2_site.p3

        for dihedral, terms in mol.PeriodicTorsionForce.items():
            try:
                other_dih = mol2.PeriodicTorsionForce[dihedral]
            except KeyError:
                other_dih = mol2.PeriodicTorsionForce[tuple(reversed(dihedral))]
            assert np.allclose(terms[:4], other_dih[:4])
Esempio n. 15
0
    def setUpClass(cls):
        """
        Create temp working directory and copy across test files.
        Initialise test molecule (acetone) with Ligand().
        """

        cls.files_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'files')
        os.mkdir('temp')
        os.chdir('temp')
        copy(os.path.join(cls.files_folder, 'acetone.pdb'), 'acetone.pdb')
        cls.molecule_pdb = Ligand('acetone.pdb')
        cls.molecule_pdb.testing = True
        copy(os.path.join(cls.files_folder, 'acetone.mol2'), 'acetone.mol2')
        cls.molecule_mol2 = Ligand('acetone.mol2')
        cls.molecule_mol2.testing = True
        os.chdir('../')
Esempio n. 16
0
def test_from_smiles(smiles):
    """
    Make sure hydrogens are added to a molecule when needed.
    """
    mol = Ligand.from_smiles(smiles_string=smiles, name="methane")
    # count the number of hydrogens
    hs = sum([1 for atom in mol.atoms if atom.atomic_symbol == "H"])
    assert hs == 4
Esempio n. 17
0
def test_add_conformers(file_name):
    """
    Load up the bace pdb and then add conformers to it from other file types.
    """
    mol = Ligand.from_file(file_name=get_data("bace0.pdb"))
    mol.coordinates = None
    mol.add_conformer(file_name=get_data(file_name))
    assert mol.coordinates.shape == (mol.n_atoms, 3)
Esempio n. 18
0
def test_to_mapped_smiles():
    """
    Make sure the the mapped smiles flag is respected.
    """
    mol = Ligand.from_file(file_name=get_data("bace0.sdf"))
    no_map = mol.to_smiles(isomeric=True, explicit_hydrogens=True, mapped=False)
    mapped = mol.to_smiles(isomeric=True, explicit_hydrogens=True, mapped=True)
    assert no_map != mapped
Esempio n. 19
0
def test_make_unique_names():
    """
    After loading a molecule with non unique atom names make sure a unique set is automatically generated.
    """
    # load the molecule with missing names
    mol = Ligand.from_file(get_data("missing_names.pdb"))
    # make sure they have been converted
    assert mol.has_unique_atom_names is True
Esempio n. 20
0
    def setUpClass(cls):
        cls.files_folder = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'files')

        # Make the temp folder and move there with the required files
        os.mkdir('temp')
        os.chdir('temp')
        copy(os.path.join(cls.files_folder, 'acetone.pdb'), 'acetone.pdb')
        cls.molecule = Ligand('acetone.pdb')
Esempio n. 21
0
def test_ligand_from_file(file_name):
    """
    For the given file type make sure rdkit can parse it and return the molecule.
    """
    mol = Ligand.from_file(file_name=get_data(file_name))
    assert mol.n_atoms > 1
    assert mol.n_bonds > 1
    assert mol.name is not None
Esempio n. 22
0
    def setUpClass(cls):
        """
        Write the big string above to a file to be used for testing.
        Cannot use actual files as pathing causes issues.
        """
        with open('acetone.pdb', 'w+') as pdb_test_file:
            pdb_test_file.write(acetone)

        cls.molecule = Ligand('acetone.pdb')
Esempio n. 23
0
def test_rotatable_bonds():
    """
    Make sure we can find true rotatable bonds for a molecule.
    """
    mol = Ligand.from_file(file_name=get_data("biphenyl.pdb"))
    assert mol.rotatable_bonds == [
        (3, 4),
    ]
    assert mol.n_rotatable_bonds == 1
Esempio n. 24
0
    def load_molecule(self):
        """Load the molecule into the gui and make an instance of the Ligand class."""

        # Open up the file explorer
        filename = self.load_file(["pdb", "mol2", "mol", "sdf"])
        if ".pdb" in filename or ".mol2" in filename or "mol" in filename:
            # Instance the QUBEKit class
            self.molecule = Ligand(filename)
            self.viewer.load_molecule(filename)
            self.ligand_name.setText(f"{self.molecule.name}")
Esempio n. 25
0
    def load_molecule(self):
        """Load the molecule into the gui and make an instance of the Ligand class."""

        # Open up the file explorer
        filename = self.load_file(['pdb', 'mol2', 'mol', 'sdf'])
        if '.pdb' in filename or '.mol2' in filename or 'mol' in filename:
            # Instance the QUBEKit class
            self.molecule = Ligand(filename)
            self.viewer.load_molecule(filename)
            self.ligand_name.setText(f'{self.molecule.name}')
Esempio n. 26
0
def test_parametrise_none(tmpdir):
    """
    If no engine is passed make sure we init the parameter holders but store nothing.
    """
    with tmpdir.as_cwd():
        mol = Ligand.from_file(get_data("acetone.pdb"))
        mol.parameter_engine = "none"
        param_mol = Execute.parametrise(molecule=mol, verbose=False)
        for i in range(param_mol.n_atoms):
            assert param_mol.NonbondedForce[i] == [0, 0, 0]
Esempio n. 27
0
def test_parametrise_missing_file(tmpdir):
    """
    If a missing file is provided make sure an error is raised.
    """
    with tmpdir.as_cwd():
        mol = Ligand.from_file(get_data("acetone.pdb"))
        mol.home = os.getcwd()
        mol.parameter_engine = "xml"
        with pytest.raises(FileNotFoundError):
            _ = Execute.parametrise(molecule=mol, verbose=False)
Esempio n. 28
0
def test_to_topology(molecule):
    """
    Make sure that a topology generated using qubekit matches an openff one.
    """
    mol = Ligand.from_file(file_name=get_data(molecule))
    offmol = OFFMolecule.from_file(file_path=get_data(molecule))
    assert (
        nx.algorithms.isomorphism.is_isomorphic(mol.to_topology(), offmol.to_networkx())
        is True
    )
Esempio n. 29
0
def test_to_smiles_isomeric():
    """
    Make sure we can write out smiles strings with the correct settings.
    """
    # use bace as it has a chiral center
    mol = Ligand.from_file(file_name=get_data("bace0.sdf"))
    smiles = mol.to_smiles(isomeric=True, explicit_hydrogens=False, mapped=False)
    assert "@@" in smiles
    smiles = mol.to_smiles(isomeric=False, explicit_hydrogens=False, mapped=False)
    assert "@" not in smiles
    def setUpClass(cls):
        """Create temp working directory and copy across test files."""

        cls.files_folder = os.path.join(
            os.path.dirname(os.path.abspath(__file__)), 'files')

        os.mkdir('temp')
        os.chdir('temp')
        copy(os.path.join(cls.files_folder, 'acetone.pdb'), 'acetone.pdb')
        cls.molecule = Ligand('acetone.pdb')
        cls.molecule.testing = True