Ejemplo n.º 1
0
    def _set_target_distances(self):
        '''
        Called before TS refinement to compute all
        target bonding distances. These are only returned
        if that pairing is not a non-covalent interaction,
        that is if pairing was not specified with letters
        "x", "y" or "z".
        '''
        self.target_distances = {}

        r_atoms = {}
        for mol in self.objects:
            for letter, r_atom in mol.reactive_atoms_classes_dict[0].items():
                if letter not in ("x", "y", "z"):
                    r_atoms[r_atom.cumnum] = r_atom

        pairings = self.constrained_indexes.ravel()
        pairings = pairings.reshape(int(pairings.shape[0] / 2), 2)
        pairings = {tuple(sorted((a, b))) for a, b in pairings}

        active_pairs = [
            indexes for letter, indexes in self.pairings_table.items()
            if letter not in ("x", "y", "z")
        ]

        for index1, index2 in pairings:

            if [index1, index2] in active_pairs:

                if hasattr(self, 'pairings_dists'):
                    letter = list(
                        self.pairings_table.keys())[active_pairs.index(
                            [index1, index2])]

                    if letter in self.pairings_dists:
                        self.target_distances[(
                            index1, index2)] = self.pairings_dists[letter]
                        continue
                # if target distance has been specified by user, read that, otherwise compute it

                r_atom1 = r_atoms[index1]
                r_atom2 = r_atoms[index2]

                dist1 = orb_dim_dict.get(r_atom1.symbol + ' ' + str(r_atom1),
                                         orb_dim_dict['Fallback'])
                dist2 = orb_dim_dict.get(r_atom2.symbol + ' ' + str(r_atom2),
                                         orb_dim_dict['Fallback'])

                self.target_distances[(index1, index2)] = dist1 + dist2
Ejemplo n.º 2
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:
        '''
        '''
        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)
        


        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.others = mol.atomcoords[conf][neighbors_indexes]

        self.vectors = self.others - self.coord # vectors connecting reactive atom with neighbors
        self.orb_vec = norm(np.mean(np.array([np.cross(norm(self.vectors[0]), norm(self.vectors[1])),
                                              np.cross(norm(self.vectors[1]), norm(self.vectors[2])),
                                              np.cross(norm(self.vectors[2]), norm(self.vectors[0]))]), axis=0))

        self.orb_vecs = np.vstack((self.orb_vec, -self.orb_vec))

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')
            
            self.center = self.orb_vecs * orb_dim      

            self.center += self.coord
Ejemplo n.º 3
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:
        '''
        '''
        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)
        


        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.others = mol.atomcoords[conf][neighbors_indexes]

        self.vectors = self.others - self.coord # vector connecting center to substituent

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')
        
            if mol.sigmatropic[conf]:
                # two p lobes
                p_lobe = norm(np.cross(self.vectors[0], self.vectors[1]))*orb_dim
                self.orb_vecs = np.concatenate(([p_lobe], [-p_lobe]))

            else:
                # lone pair lobe
                self.orb_vecs = np.array([-norm(np.mean([norm(v) for v in self.vectors], axis=0))*orb_dim])

            self.center = self.orb_vecs + self.coord
Ejemplo n.º 4
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:
        '''
        '''
        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)
        


        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.others = mol.atomcoords[conf][neighbors_indexes]

        self.orb_vecs = self.others - self.coord # vectors connecting center to each of the two substituents

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')

            self.orb_vecs = orb_dim * np.array([norm(v) for v in self.orb_vecs]) # making both vectors a fixed, defined length

            orb_mat = rot_mat_from_pointer(np.mean(self.orb_vecs, axis=0), 90) @ rot_mat_from_pointer(np.cross(self.orb_vecs[0], self.orb_vecs[1]), 180)

            # self.orb_vecs = np.array([orb_mat @ v for v in self.orb_vecs])
            self.orb_vecs = (orb_mat @ self.orb_vecs.T).T
            
            self.center = self.orb_vecs + self.coord
Ejemplo n.º 5
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:
        '''
        '''
        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)

        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.other = mol.atomcoords[conf][neighbors_indexes][0]

        if not mol.sp3_sigmastar:
            self.orb_vecs = np.array([norm(self.coord - self.other)])

        else:
            other_reactive_indexes = list(mol.reactive_indexes)
            other_reactive_indexes.remove(i)
            for index in other_reactive_indexes:
                    if index in neighbors_indexes:
                        parnter_index = index
                        break
            # obtain the reference partner index

            partner = mol.atomcoords[conf][parnter_index]
            pivot = norm(partner - self.coord)

            neighbors_of_partner = neighbors(mol.graph, parnter_index)
            neighbors_of_partner.remove(i)
            orb_vec = norm(mol.atomcoords[conf][neighbors_of_partner[0]] - partner)
            orb_vec = orb_vec - orb_vec @ pivot * pivot

            steps = 3 # number of total orbitals
            self.orb_vecs = np.array([rot_mat_from_pointer(pivot, angle+60) @ orb_vec for angle in range(0,360,int(360/steps))])
            # orbitals are staggered in relation to sp3 substituents

            self.orb_vers = norm(self.orb_vecs[0])

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)

                if orb_dim is None:
                    orb_dim = norm_of(self.coord - self.other)
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using the bonding distance ({round(orb_dim, 3)} A).')

            self.center = orb_dim * self.orb_vecs + self.coord
Ejemplo n.º 6
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:

        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)
        
        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]

        self.coord = mol.atomcoords[conf][i]
        self.others = mol.atomcoords[conf][neighbors_indexes]

        self.vectors = self.others - self.coord # vector connecting center to substituent


        angle = vec_angle(norm(self.others[0] - self.coord),
                          norm(self.others[1] - self.coord))
        
        if np.abs(angle - 180) < 5:
            self.type = 'sp'

        else:
            self.type = 'bent carbene'

        self.allene = False
        self.ketene = False
        if self.type == 'sp' and all([s == 'C' for s in self.neighbors_symbols]):

            neighbors_of_neighbors_indexes = (neighbors(mol.graph, neighbors_indexes[0]),
                                              neighbors(mol.graph, neighbors_indexes[1]))

            neighbors_of_neighbors_indexes[0].remove(i)
            neighbors_of_neighbors_indexes[1].remove(i)

            if (len(side1) == len(side2) == 2 for side1, side2 in neighbors_of_neighbors_indexes):
                self.allene = True

        elif self.type == 'sp' and sorted(self.neighbors_symbols) in (['C', 'O'], ['C', 'S']):

            self.ketene = True

            neighbors_of_neighbors_indexes = (neighbors(mol.graph, neighbors_indexes[0]),
                                              neighbors(mol.graph, neighbors_indexes[1]))

            neighbors_of_neighbors_indexes[0].remove(i)
            neighbors_of_neighbors_indexes[1].remove(i)
                
            if len(neighbors_of_neighbors_indexes[0]) == 2:
                substituent = mol.atomcoords[conf][neighbors_of_neighbors_indexes[0][0]]
                ketene_atom = mol.atomcoords[conf][neighbors_indexes[0]]
                self.ketene_ref = substituent - ketene_atom

            elif len(neighbors_of_neighbors_indexes[1]) == 2:
                substituent = mol.atomcoords[conf][neighbors_of_neighbors_indexes[1][0]]
                ketene_atom = mol.atomcoords[conf][neighbors_indexes[1]]
                self.ketene_ref = substituent - ketene_atom

            else:
                self.ketene = False

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + self.type
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')
        
            if self.type == 'sp':

                v = np.random.rand(3)
                pivot1 = v - ((v @ norm(self.vectors[0])) * self.vectors[0])

                if self.allene or self.ketene:
                    # if we have an allene or ketene, pivot1 is aligned to
                    # one substituent so that the resulting positions
                    # for the four orbital centers make chemical sense.

                    axis = norm(self.others[0] - self.others[1])
                    # versor connecting reactive atom neighbors
                    
                    if self.allene:
                        ref = (mol.atomcoords[conf][neighbors_of_neighbors_indexes[0][0]] -
                               mol.atomcoords[conf][neighbors_indexes[0]])
                    else:
                        ref = self.ketene_ref

                    pivot1 = ref - ref @ axis * axis
                    # projection of ref orthogonal to axis (vector rejection)


                pivot2 = norm(np.cross(pivot1, self.vectors[0]))
                        
                self.orb_vecs = np.array([rot_mat_from_pointer(pivot2, 90) @
                                          rot_mat_from_pointer(pivot1, angle) @
                                          norm(self.vectors[0]) for angle in (0, 90, 180, 270)]) * orb_dim

                self.center = self.orb_vecs + self.coord
                # four vectors defining the position of the four orbital lobes centers



            else: # bent carbene case: three centers, sp2+p
                
                self.orb_vecs = np.array([-norm(np.mean([norm(v) for v in self.vectors], axis=0))*orb_dim])
                # one sp2 center first

                p_vec = np.cross(norm(self.vectors[0]), norm(self.vectors[1]))
                p_vecs = np.array([norm(p_vec)*orb_dim, -norm(p_vec)*orb_dim])
                self.orb_vecs = np.concatenate((self.orb_vecs, p_vecs))
                # adding two p centers

                self.center = self.orb_vecs + self.coord
Ejemplo n.º 7
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:
        '''
        '''
        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)       


        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.other = mol.atomcoords[conf][neighbors_indexes][0]

        self.vector = self.other - self.coord # vector connecting center to substituent

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')

            neighbors_of_neighbor_indexes = neighbors(mol.graph, neighbors_indexes[0])
            neighbors_of_neighbor_indexes.remove(i)

            self.vector = norm(self.vector)*orb_dim

            if len(neighbors_of_neighbor_indexes) == 2:
            # if it is a normal ketone (or an enolate), n orbital lobes must be coplanar with
            # atoms connecting to ketone C atom, or p lobes must be placed accordingly

                a1 = mol.atomcoords[conf][neighbors_of_neighbor_indexes[0]]
                a2 = mol.atomcoords[conf][neighbors_of_neighbor_indexes[1]]
                pivot = norm(np.cross(a1 - self.coord, a2 - self.coord))

                if mol.sigmatropic[conf]:
                    # two p lobes
                    self.center = np.concatenate(([pivot*orb_dim], [-pivot*orb_dim]))

                else:
                    #two n lobes
                    self.center = np.array([rot_mat_from_pointer(pivot, angle) @ self.vector for angle in (120,240)])

            elif len(neighbors_of_neighbor_indexes) in (1, 3):
            # ketene or alkoxide
            
                ketene_sub_indexes = neighbors(mol.graph, neighbors_of_neighbor_indexes[0])
                ketene_sub_indexes.remove(neighbors_indexes[0])

                ketene_sub_coords = mol.atomcoords[conf][ketene_sub_indexes[0]]
                n_o_n_coords = mol.atomcoords[conf][neighbors_of_neighbor_indexes[0]]

                # vector connecting ketene R with C (O=C=C(R)R)
                v = (ketene_sub_coords - n_o_n_coords)

                # this vector is orthogonal to the ketene O=C=C and coplanar with the ketene
                pointer = v - ((v @ norm(self.vector)) * self.vector)
                pointer = norm(pointer) * orb_dim

                self.center = np.array([rot_mat_from_pointer(self.vector, 90*step) @ pointer for step in range(4)])
            
        
            self.orb_vecs = np.array([norm(center) for center in self.center])
            # unit vectors connecting reactive atom coord with orbital centers

            self.center += self.coord
Ejemplo n.º 8
0
    def init(self, mol, i, update=False, orb_dim=None, conf=0) -> None:

        self.index = i
        self.symbol = pt[mol.atomnos[i]].symbol
        neighbors_indexes = neighbors(mol.graph, i)
        self.neighbors_symbols = [pt[mol.atomnos[i]].symbol for i in neighbors_indexes]
        self.coord = mol.atomcoords[conf][i]
        self.others = mol.atomcoords[conf][neighbors_indexes]
        
        if not mol.sp3_sigmastar:

            if not hasattr(self, 'leaving_group_index'):
                self.leaving_group_index = None

            if len([atom for atom in self.neighbors_symbols if atom in ['O', 'N', 'Cl', 'Br', 'I']]) == 1: # if we can tell where is the leaving group
                self.leaving_group_coords = self.others[self.neighbors_symbols.index([atom for atom in self.neighbors_symbols if atom in ['O', 'Cl', 'Br', 'I']][0])]

            elif len([atom for atom in self.neighbors_symbols if atom not in ['H']]) == 1: # if no clear leaving group but we only have one atom != H
                self.leaving_group_coords = self.others[self.neighbors_symbols.index([atom for atom in self.neighbors_symbols if atom not in ['H']][0])]

            else: # if we cannot infer, ask user if we didn't have already 
                try:
                    self.leaving_group_coords = self._set_leaving_group(mol, neighbors_indexes)

                except Exception:
                # if something goes wrong, we fallback to command line input for reactive atom index collection

                    if self.leaving_group_index is None:

                        while True:

                            self.leaving_group_index = input(f'Please insert the index of the leaving group atom bonded to the sp3 reactive atom (index {self.index}) of molecule {mol.rootname} : ')
                            
                            if self.leaving_group_index == '' or self.leaving_group_index.lower().islower():
                                pass
                            
                            elif int(self.leaving_group_index) in neighbors_indexes:
                                self.leaving_group_index = int(self.leaving_group_index)
                                break

                            else:
                                print(f'Atom {self.leaving_group_index} is not bonded to the sp3 center with index {self.index}.')
                    
                    self.leaving_group_coords = self.others[neighbors_indexes.index(self.leaving_group_index)]

            self.orb_vecs = np.array([self.coord - self.leaving_group_coords])
            self.orb_vers = norm(self.orb_vecs[0])

        else: # Sigma bond type

            other_reactive_indexes = list(mol.reactive_indexes)
            other_reactive_indexes.remove(i)
            for index in other_reactive_indexes:
                    if index in neighbors_indexes:
                        parnter_index = index
                        break
            # obtain the reference partner index

            pivot = norm(mol.atomcoords[conf][parnter_index] - self.coord)

            other_neighbors = deepcopy(neighbors_indexes)
            other_neighbors.remove(parnter_index)
            orb_vec = norm(mol.atomcoords[conf][other_neighbors[0]] - self.coord)
            orb_vec = orb_vec - orb_vec @ pivot * pivot

            steps = 3 # number of total orbitals
            self.orb_vecs = np.array([rot_mat_from_pointer(pivot, angle+60) @ orb_vec for angle in range(0,360,int(360/steps))])
            # orbitals are staggered in relation to sp3 substituents

            self.orb_vers = norm(self.orb_vecs[0])

        if update:
            if orb_dim is None:
                key = self.symbol + ' ' + str(self)
                orb_dim = orb_dim_dict.get(key)
                
                if orb_dim is None:
                    orb_dim = orb_dim_dict['Fallback']
                    print(f'ATTENTION: COULD NOT SETUP REACTIVE ATOM ORBITAL FROM PARAMETERS. We have no parameters for {key}. Using {orb_dim} A.')

            self.center = np.array([orb_dim * norm(vec) + self.coord for vec in self.orb_vecs])