Ejemplo n.º 1
0
def get_linker_atoms_and_cost(linker, template_linker, current_atoms, x_coords=None):
    """
    Get the xyzs of a linker that is fitted to a template_linker object and
    the associated cost function – i.e. the repulsion to the current cage
    structure

    :param linker: (Linker)
    :param template_linker: (Template.Linker)
    :param curr_coords: (list(list))
    :return: list(list)), float
    """

    if x_coords is None:
        x_coords = linker.get_xmotif_coordinates()

    # Ensure the shift amount dr is set
    if linker.dr is None:
        logger.error('Cannot build a cage dr was None')
        return linker.atoms, 9999999.9

    # Expand the template by an about dr
    shifted_coords = get_shifted_template_x_motif_coords(linker_template=template_linker,
                                                         dr=linker.dr)

    linker_coords, cost = get_fitted_linker_coords_and_cost(linker=linker,
                                                            template_x_coords=shifted_coords,
                                                            coords_to_fit=x_coords,
                                                            curr_coords=[atom.coord for atom in current_atoms])
    logger.info(f'Repulsive + fitting cost for adding the linker is {cost:.5f}')

    atoms = [Atom(linker.atoms[i].label, coord=linker_coords[i])
             for i in range(linker.n_atoms)]

    return atoms, cost
Ejemplo n.º 2
0
def xyzfile_to_atoms(filename):
    """
    Convert a standard xyz file into a list of atoms

    :param filename: (str)
    :return: (list(cgbind.atoms.Atom))
    """
    logger.info(f'Converting {filename} to list of atoms')

    if not filename.endswith('.xyz'):
        logger.error('Could not read .xyz file')
        raise FileMalformatted

    atoms = []

    with open(filename, 'r') as xyz_file:
        xyz_lines = xyz_file.readlines()[2:]
        for line in xyz_lines:
            atom_label, x, y, z = line.split()
            atoms.append(Atom(atom_label, float(x), float(y), float(z)))

    if len(atoms) == 0:
        logger.error(f'Could not read xyz lines in {filename}')
        raise FileMalformatted

    return atoms
Ejemplo n.º 3
0
def build_heteroleptic_cage(cage, max_cost):
    logger.info('Building a heteroleptic cage')
    logger.warning('Due to the very large space that needs to be minimised '
                   'only the *best* linker conformer is used')

    added_linkers, atoms = [], []

    for i, linker in enumerate(cage.linkers):

        linker.set_ranked_linker_possibilities(metal=cage.metal)

        linker_atoms, cost = get_linker_atoms_and_cost(linker.possibilities[0],
                                                       cage.cage_template.linkers[i],
                                                       atoms)

        logger.info(f'L-L repulsion + fit to template in building cage is {cost:.2f}')
        atoms += linker_atoms
        linker.dr = linker.possibilities[0].dr

    logger.warning('Heteroleptic cages will have the average dr of all linkers '
                   '- using the average')
    cage.dr = np.average(np.array([linker.dr for linker in cage.linkers]))

    # Add the metals from the template shifted by dr
    for metal in cage.cage_template.metals:
        metal_coord = cage.dr * metal.shift_vec / np.linalg.norm(metal.shift_vec) + metal.coord
        atoms.append(Atom(cage.metal, x=metal_coord[0], y=metal_coord[1], z=metal_coord[2]))

    cage.set_atoms(atoms)
    return None
Ejemplo n.º 4
0
def extract_conformers_from_rdkit_mol_object(mol_obj, conf_ids):
    """
    Generate xyz lists for all the conformers in conf_ids
    :param mol_obj: Molecule object
    :param conf_ids: (list) list of conformer ids to convert to xyz
    :return: (list(list(cgbind.atoms.Atom)))
    """
    conformers = []

    for i in range(len(conf_ids)):
        mol_block_lines = Chem.MolToMolBlock(mol_obj,
                                             confId=conf_ids[i]).split('\n')
        atoms = []

        for line in mol_block_lines:
            split_line = line.split()
            if len(split_line) == 16:
                atom_label, x, y, z = split_line[3], split_line[0], split_line[
                    1], split_line[2]
                atoms.append(Atom(atom_label, float(x), float(y), float(z)))

        conformer = BaseStruct()
        conformer.set_atoms(atoms)
        conformers.append(conformer)

    if len(conformers) == 0:
        raise CgbindCritical(
            'Length of conformer xyz list was 0. RDKit failed')

    return conformers
Ejemplo n.º 5
0
def test_atoms():

    assert atoms.get_atomic_number(atom=Atom('P', 0.0, 0.0, 0.0)) == 15
    # For unknown atoms types the default atomic number is 6
    assert atoms.get_atomic_number(atom=Atom('XX', 0.0, 0.0, 0.0)) == 6

    assert 11.99 < atoms.get_atomic_mass(atom=Atom('C', 0.0, 0.0, 0.0)) < 12.02
    # For unknown atoms types the default atomic mass is 10
    assert 9 < atoms.get_atomic_mass(atom=Atom('XX', 0.0, 0.0, 0.0)) < 11

    assert 1.1 < atoms.get_vdw_radii(atom=Atom('H', 0.0, 0.0, 0.0)) < 1.3
    # For unknown atoms types the default van der Walls radii is 2.0 Å
    assert 1.9 < atoms.get_vdw_radii(atom=Atom('XX', 0.0, 0.0, 0.0)) < 2.1

    assert atoms.get_max_valency(atom=Atom('H', 0.0, 0.0, 0.0)) == 1
    assert atoms.get_max_valency(atom=Atom('C', 0.0, 0.0, 0.0)) == 4
    assert atoms.get_max_valency(atom=Atom('XX', 0.0, 0.0, 0.0)) == 6
Ejemplo n.º 6
0
def test_reasonable_geom():

    tmp = Molecule()
    tmp.set_atoms([Atom('H', 0.0, 0.0, 0.0), Atom('H', 0.1, 0.0, 0.0)])
    assert geom.is_geom_reasonable(tmp) is False
    tmp.set_atoms([Atom('H', 0.0, 0.0, 0.0), Atom('H', 1.0, 0.0, 0.0)])
    assert geom.is_geom_reasonable(tmp) is True
    tmp.set_atoms([Atom('H', 0.0, 0.0, 0.0), Atom('H', 1001, 0.0, 0.0)])
    assert geom.is_geom_reasonable(tmp) is False
Ejemplo n.º 7
0
def molfile_to_atoms(filename):
    """
    Convert a .mol file to a list of atoms

    :param filename: (str)
    :return: (list(Atom))
    """
    """
    e.g. for methane:
    _____________________

     OpenBabel03272015013D

      5  4  0  0  0  0  0  0  0  0999 V2000
       -0.2783    0.0756    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
        0.7917    0.0756    0.0000 H   0  0  0  0  0  0  0  0  0  0  0  0
       -0.6349   -0.9294   -0.0876 H   0  0  0  0  0  0  0  0  0  0  0  0
       -0.6349    0.6539   -0.8266 H   0  0  0  0  0  0  0  0  0  0  0  0
       -0.6349    0.5022    0.9141 H   0  0  0  0  0  0  0  0  0  0  0  0
      1  2  1  0  0  0  0
      1  3  1  0  0  0  0
      1  4  1  0  0  0  0
      1  5  1  0  0  0  0
    M  END
    _____________________
    """
    atoms = []

    if not filename.endswith('.mol'):
        logger.error('Could not read .mol file')
        raise FileMalformatted

    with open(filename, 'r') as mol_file:
        mol_lines = mol_file.readlines()[3:]
        try:
            n_atoms = int(mol_lines[0].split()[0])

        except ValueError:
            raise FileMalformatted

        for line in mol_lines[1:n_atoms + 1]:
            x, y, z, atom_label = line.split()[:4]
            atoms.append(Atom(atom_label, float(x), float(y), float(z)))

    if len(atoms) == 0:
        logger.error(f'Could not read xyz lines in {filename}')
        raise FileMalformatted

    return atoms
Ejemplo n.º 8
0
def xyz_file_to_atoms(filename):
    """/
    From an .xyz file get a list of atoms

    :param filename: (str) .xyz filename
    :return: (list(Atom))
    """
    logger.info(f'Getting atoms from {filename}')

    atoms = []

    if not filename.endswith('.xyz'):
        raise FileMalformatted

    # Open the file that exists and should(!) be in the correct format
    with open(filename, 'r') as xyz_file:

        try:
            # First item in an xyz file is the number of atoms
            n_atoms = int(xyz_file.readline().split()[0])

        except IndexError:
            raise FileMalformatted

        # XYZ lines should be the following 2 + n_atoms lines
        xyz_lines = xyz_file.readlines()[1:n_atoms + 1]

        for line in xyz_lines:

            try:
                atom_label, x, y, z = line.split()[:4]
                atoms.append(Atom(atomic_symbol=atom_label, x=x, y=y, z=z))

            except (IndexError, TypeError, ValueError):
                raise FileMalformatted

    return atoms
Ejemplo n.º 9
0
def build_homoleptic_cage(cage, max_cost):
    """
    Construct the geometry (atoms) of a homoleptic cage

    :param cage: (Cage)
    :param max_cost: (float) Maximum cost to break out of the loop over
    :return:
    """

    # Get the list of Linkers ordered by the best fit to the template
    cage.linkers[0].set_ranked_linker_possibilities(metal=cage.metal)
    logger.info(f'Have {len(cage.linkers[0].possibilities)} linkers to fit')

    min_cost, best_linker = 99999999.9, None
    atoms, cage_cost = [], 99999999.9

    # For all the possible linker conformer / Xmotif set possibilities
    for linker in cage.linkers[0].possibilities:

        # Atoms for and cost in building this cage
        atoms, cage_cost = [], 0.0

        # Coordinates of the X motif atoms in this linker - used to rotate
        x_coords = linker.get_xmotif_coordinates()

        for i, template_linker in enumerate(cage.cage_template.linkers):
            linker_atoms, cost = get_linker_atoms_and_cost(linker,
                                                           template_linker,
                                                           atoms,
                                                           x_coords)
            cage_cost += cost
            atoms += linker_atoms

        if cage_cost < min_cost:
            min_cost = cage_cost
            best_linker = deepcopy(linker)

        if cage_cost < max_cost:
            logger.info(f'Total L-L repulsion + fit to template in building '
                        f'cage is {cage_cost:.2f}')
            break

    # If there is no break due to a small repulsion then build the best
    # possible cage
    if cage_cost > max_cost:
        if best_linker is None:
            logger.error('Could not achieve the required cost threshold for '
                         'building the cage')
            return None
        else:
            logger.warning('Failed to reach the threshold. Returning the cage '
                           'that minimises the L-L repulsion')
            atoms = []
            for i, template_linker in enumerate(cage.cage_template.linkers):
                linker_atoms, _ = get_linker_atoms_and_cost(best_linker,
                                                            template_linker,
                                                            atoms)
                atoms += linker_atoms

    # Set the delta r for the whole cage
    cage.dr = best_linker.dr

    # Add the metals from the template shifted by dr
    for metal in cage.cage_template.metals:

        if cage.dr is None:
            raise CannotBuildCage('Cage had no shift distance (∆r)')

        if metal.shift_vec is None:
            raise CannotBuildCage('Template shift vector not defined')

        metal_coord = cage.dr * metal.shift_vec / np.linalg.norm(metal.shift_vec) + metal.coord
        atoms.append(Atom(cage.metal, coord=metal_coord))

    cage.set_atoms(atoms)
    return None
Ejemplo n.º 10
0
def mol2file_to_atoms(filename):
    """
    Convert a .mol file into a standard set of atoms

    :param filename: (str)
    :return: (lis(Atom))
    """
    logger.info('Converting .mol2 file to atoms')

    if not filename.endswith('.mol2'):
        logger.error('Could not read .mol2 file')
        raise FileMalformatted

    mol_file_lines = open(filename, 'r').readlines()

    # Get the unformatted atoms from the .mol2 file. The atom labels will not
    # be standard
    atoms, xyz_block = [], False
    for n_line, line in enumerate(mol_file_lines):

        if '@' in line and xyz_block:
            break

        if xyz_block:
            try:
                atom_label, x, y, z = line.split()[1:5]
                try:
                    atoms.append(Atom(atom_label, float(x), float(y),
                                      float(z)))
                except TypeError:
                    logger.error('There was a problem with the .mol2 file')
                    raise FileMalformatted

            except IndexError:
                logger.error('There was a problem with the .mol2 file')
                raise FileMalformatted

        # e.g.   @<TRIPOS>ATOM
        #        1 Pd1     -2.1334  12.0093  11.5778   Pd        1 RES1   2.0000
        if '@' in line and 'ATOM' in line and len(
                mol_file_lines[n_line + 1].split()) == 9:
            xyz_block = True

    # Fix any atom labels
    for atom in atoms:

        if len(atom.label) == 1:
            continue

        # e.g. P1 or C58
        elif atom.label[0].isalpha() and not atom.label[1].isalpha():
            atom.label = atom.label[0]

        # e.g. Pd10
        elif atom.label[0].isalpha() and atom.label[1].isalpha():
            atom.label = atom.label[:2].title()

        else:
            logger.error('Unrecognised atom type')
            raise FileMalformatted

    return atoms
Ejemplo n.º 11
0
def test_atom_class():

    x = Atom('X', 0.0, 0.0, 0.0)
    assert x.coord.shape == (3, )
    assert all(x.coord == np.zeros(3))
    assert len(str(x).split()) == 4  # Atom symbol, x, y, z
Ejemplo n.º 12
0
from cgbind import geom
import numpy as np
from cgbind.atoms import Atom
from cgbind.molecule import Molecule

h2_atoms = [Atom('H', 0.0, 0.0, 0.0), Atom('H', 1.0, 0.0, 0.0)]


def test_com():
    com = geom.calc_com(atoms=h2_atoms)
    ideal_com = np.array([0.5, 0.0, 0.0])
    assert np.abs(np.average(com - ideal_com)) < 1E-5


def test_normed_vector():

    coord1 = np.array([0.0, 0.0, 0.0])
    coord2 = np.array([2.0, 0.0, 0.0])

    ideal_normed_vector = np.array([1.0, 0.0, 0.0])
    normed_vector = geom.calc_normalised_vector(coord1, coord2)

    assert np.abs(np.average(ideal_normed_vector - normed_vector)) < 1E-5


def test_rot_matix():

    axis = np.array([0.0, 0.0, 1.0])
    theta = np.pi  # angle in radians

    rot_matix = geom.rotation_matrix(axis, theta)