def dfs(at1: Atom, atom_set: Set[Atom]): at1._visited = True atom_set.add(at1) for at2 in at1.neighbors(): if at2._visited: continue dfs(at2, atom_set)
def set_dihed(self, angle: float, anchor: Atom, cap: Sequence[Atom], opt: bool = True, unit: str = 'degree') -> None: """Change all valid dihedral angles into a specific value. Performs an inplace update of this instance. Parameters ---------- angle : :class:`float` The desired dihedral angle. anchor : |plams.Atom| The ligand anchor atom. opt : :class:`bool` Whether or not the dihedral adjustment should be followed up by an RDKit UFF optimization. unit : :class:`str` The input unit. """ cap_atnum = [] for at in cap: cap_atnum.append(at.atnum) at.atnum = 0 angle = Units.convert(angle, unit, 'degree') bond_iter = (bond for bond in self.bonds if bond.atom1.atnum != 1 and bond.atom2.atnum != 1 and bond.order == 1 and not self.in_ring(bond)) # Correction factor for, most importantly, tri-valent anchors (e.g. P(R)(R)R) dihed_cor = angle / 2 neighbors = anchor.neighbors() if len(neighbors) > 2: atom_list = [anchor] + sorted(neighbors, key=lambda at: -at.atnum)[:3] improper = get_dihed(atom_list) dihed_cor *= np.sign(improper) for bond in bond_iter: # Gather lists of all non-hydrogen neighbors n1, n2 = self.neighbors_mod(bond.atom1), self.neighbors_mod(bond.atom2) # Remove all atoms in `bond` n1 = [atom for atom in n1 if atom is not bond.atom2] n2 = [atom for atom in n2 if atom is not bond.atom1] # Remove all non-subsituted atoms # A special case consists of anchor atoms; they can stay if len(n1) > 1: n1 = [ atom for atom in n1 if (len(self.neighbors_mod(atom)) > 1 or atom is anchor or atom.atnum == 0) ] if len(n2) > 1: n2 = [ atom for atom in n2 if (len(self.neighbors_mod(atom)) > 1 or atom is anchor or atom.atnum == 0) ] # Set `bond` in an anti-periplanar conformation if n1 and n2: dihed = get_dihed((n1[0], bond.atom1, bond.atom2, n2[0])) if anchor not in bond: self.rotate_bond(bond, bond.atom1, angle - dihed, unit='degree') else: dihed -= dihed_cor self.rotate_bond(bond, bond.atom1, -dihed, unit='degree') dihed_cor *= -1 for at, atnum in zip(cap, cap_atnum): at.atnum = atnum if opt: rdmol = molkit.to_rdmol(self) UFF(rdmol).Minimize() self.from_rdmol(rdmol)
def _besides_ring(atom: Atom) -> bool: """Check if any neighboring atoms are part of a ring system.""" return any(plams_mol.in_ring(at) for at in atom.neighbors())