Exemple #1
0
def test_bond_assignment():

    # Check that if the pair isn't found then a length is still returned
    assert 1.4 < bond_lengths.get_avg_bond_length(atom_i_label='X',
                                                  atom_j_label='X') < 1.6

    # CH bond should be ~1.1 Å
    assert 0.8 < bond_lengths.get_avg_bond_length(atom_i_label='C',
                                                  atom_j_label='H') < 1.2
Exemple #2
0
def add_capping_atom(atom_index, n_atom_index, graph, s_molecule):
    """
    Add a capping atom, e.g.


              H
             /
     C_a---C_b - H   ->   C_a--H          where C_a is numbered atom_index, C_b is numbered n_atom_index
             \
              H

    Arguments:
        atom_index (int):
        n_atom_index (int):
        graph (nx.Graph): Current molecular graph of the stripped/truncated molecule
        s_molecule (autode.species.Species): Stripped molecule
    """
    logger.info(f'Swapping saturated carbon {n_atom_index} next to atom '
                f'{atom_index} for hydrogen')

    graph.add_node(n_atom_index, atom_label='H', stereo=False)

    # Relabel the atom in the stripped molecule
    s_molecule.atoms[n_atom_index].label = 'H'

    # Shift the added capping hydrogen to the 'ideal' E-H bond length
    curr_dist = s_molecule.get_distance(atom_index, n_atom_index)
    ideal_dist = get_avg_bond_length(atom_i_label=s_molecule.atoms[atom_index].label, atom_j_label='H')
    shift_vec = s_molecule.atoms[n_atom_index].coord - s_molecule.atoms[atom_index].coord

    s_molecule.atoms[n_atom_index].translate(vec=(ideal_dist - curr_dist) * shift_vec / curr_dist)

    return None
Exemple #3
0
    def set_attack_r0(self, species, shift_factor):
        """Set the ideal distance between a and c atoms in a substitution
        centre"""

        r0 = get_avg_bond_length(atom_i_label=species.atoms[self.a_atom].label,
                                 atom_j_label=species.atoms[self.c_atom].label)

        self.r0_ac = shift_factor * r0
        return None
Exemple #4
0
    def __init__(self, atom_indexes, species):
        """"
        Forming bond with current and final distances

        Arguments:
            atom_indexes (tuple(int)):
            species (autode.species.Species):
        """
        super().__init__(atom_indexes)

        i, j = self.atom_indexes
        self.curr_dist = species.get_distance(atom_i=i, atom_j=j)
        self.final_dist = get_avg_bond_length(species.atoms[i].label,
                                              species.atoms[j].label)
Exemple #5
0
def make_graph(species,
               rel_tolerance=0.25,
               bond_list=None,
               allow_invalid_valancies=False):
    """
    Make the molecular graph from the 'bonds' determined on a distance criteria
    or a smiles parser object. All attributes default to false

    Nodes attributes:
        (0) atom_label: Atomic symbol of this atom
        (1) stereo: Is this atom part of some stereochemistry e.g. R/S or E/Z

    Edge attributes:
        (1) pi: Is this bond a pi bond. If it is then there should be no
                rotation the bond axis in conformer generation
        (2) active: Is this bond being made/broken
                   (applies only to TransitionState objects)

    Arguments:
        species (autode.species.Species):

    Keyword Arguments:
        rel_tolerance (float):
        bond_list (list(tuple)):
        allow_invalid_valancies (bool):
    """
    logger.info('Generating molecular graph with NetworkX')

    graph = nx.Graph()

    # Add the atoms to the graph all are initially assumed not to be
    # stereocenters
    for i in range(species.n_atoms):
        graph.add_node(i, atom_label=species.atoms[i].label, stereo=False)

    # If bonds are specified then add edges to the graph and return
    if bond_list is not None:
        [
            graph.add_edge(bond[0], bond[1], pi=False, active=False)
            for bond in bond_list
        ]
        species.graph = graph
        return None

    else:
        # Loop over the unique pairs of atoms and add 'bonds'
        coordinates = species.get_coordinates()
        dist_mat = distance_matrix(coordinates, coordinates)

        for i in get_atom_ids_sorted_type(species):

            # Iterate through the closest atoms to atom i
            for j in np.argsort(dist_mat[i]):

                if i == j:
                    # Don't bond atoms to themselves
                    continue

                avg_bond_length = get_avg_bond_length(
                    atom_i_label=species.atoms[i].label,
                    atom_j_label=species.atoms[j].label)

                # If the distance between atoms i and j are less or equal to 1.2x average length add a 'bond'
                if dist_mat[i,
                            j] <= avg_bond_length * (1.0 + rel_tolerance) and (
                                i, j) not in graph.edges:
                    graph.add_edge(i, j, pi=False, active=False)

    species.graph = graph
    set_graph_attributes(species)

    if not allow_invalid_valancies:
        remove_bonds_invalid_valancies(species)

    return None
Exemple #6
0
    def add_stereochem(self, central_atom):
        """
        Adds stereochemistry around an atom, by placing atoms are the correct
        coordinates for the stereochemistry. For tetrahedral centres, '@'
        means looking along the bond from the first atom bonded to the centre,
        the other atoms go anticlockwise in their index order in the atoms
        list. For alkene centres, '@' means the atoms go anticlockwise in their
         index order in the atoms list (this works as the same thing is applied
        to every centre)

        Arguments:
            central_atom (int): index of the atom having stereochemistry added
                                around it
        """
        vectors_dict = {
            '@td':
            np.array(([0, 0, 1], [0, 2 * np.sqrt(2) / 3, -1 / 3],
                      [-np.sqrt(2 / 3), -np.sqrt(2) / 3,
                       -1 / 3], [np.sqrt(2 / 3), -np.sqrt(2) / 3, -1 / 3])),
            '@@td':
            np.array(([0, 0, 1], [0, 2 * np.sqrt(2) / 3, -1 / 3],
                      [np.sqrt(2 / 3), -np.sqrt(2) / 3,
                       -1 / 3], [-np.sqrt(2 / 3), -np.sqrt(2) / 3, -1 / 3])),
            '@al':
            np.array(([1.0, 0.0, 0.0], [-0.5, np.sqrt(3) / 2,
                                        0], [-0.5, -np.sqrt(3) / 2, 0])),
            '@@al':
            np.array(([1.0, 0.0, 0.0], [-0.5, -np.sqrt(3) / 2,
                                        0], [-0.5, np.sqrt(3) / 2, 0]))
        }

        vectors = vectors_dict[self.stereochem_dict[central_atom]]
        self.stereocentres.append(central_atom)

        central_translation = -1 * self.atoms[central_atom].coord
        # centre the central atom
        self.shift_atom(central_atom, central_translation)

        bonded_atoms = []
        for i, bond in enumerate(self.bonds):
            # get the bonded atom in the order meant for the stereochemistry

            if bond[0] == central_atom:
                bonded_atom = bond[1]

            elif bond[1] == central_atom:
                bonded_atom = bond[0]

            else:
                continue

            if self.stereochem_dict[central_atom] in ['@td', '@@td']:
                # for tetrahedral centres, this is how the ordering is defined
                if self.atoms[bonded_atom].label == 'H':
                    if bonded_atoms[0] < central_atom:
                        bonded_atoms.insert(1, bonded_atom)
                    else:
                        bonded_atoms.insert(0, bonded_atom)

                elif i in self.ring_closing_bond_list:
                    bonded_atoms.insert(0, bonded_atom)

                else:
                    bonded_atoms.append(bonded_atom)

            else:
                bonded_atoms.append(bonded_atom)

        for i, bonded_atom in enumerate(bonded_atoms):
            if bonded_atom in self.stereocentres:
                # don't want to lose the orientation around the old
                # stereocentres, so rotate it into the right position
                self.rotate_stereocluster(central_atom, bonded_atom,
                                          vectors[i])

            bond_length = get_avg_bond_length(self.atoms[central_atom].label,
                                              self.atoms[bonded_atom].label)
            bonded_translation = (bond_length * vectors[i] -
                                  self.atoms[bonded_atom].coord)

            self.shift_atom(bonded_atom, bonded_translation, central_atom)

        self.add_cluster([central_atom] + bonded_atoms)