Пример #1
0
def to_rdkit_mol_without_metals(mol, metal_atoms, metal_bonds):
    """
    Create :class:`rdkit.Mol` with metals replaced by H atoms.

    Parameters
    ----------
    mol : :class:`.Molecule`
        The molecule to be optimized.

    metal_atoms : :class:`.list` of :class:`stk.Atom`
        List of metal atoms.

    metal_bonds : :class:`.list` of :class:`stk.Bond`
        List of bonds including metal atoms.

    Returns
    -------
    edit_mol : :class:`rdkit.Mol`
        RDKit molecule with metal atoms replaced with H atoms.

    """
    edit_mol = rdkit.EditableMol(rdkit.Mol())
    for atom in mol.get_atoms():
        if atom in metal_atoms:
            # In place of metals, add H's that will be constrained.
            # This allows the atom ids to not be changed.
            rdkit_atom = rdkit.Atom(1)
            rdkit_atom.SetFormalCharge(0)
        else:
            rdkit_atom = rdkit.Atom(atom.get_atomic_number())
            rdkit_atom.SetFormalCharge(atom.get_charge())
        edit_mol.AddAtom(rdkit_atom)

    for bond in mol.get_bonds():
        if bond in metal_bonds:
            # Do not add bonds to metal atoms (replaced with H's).
            continue
        edit_mol.AddBond(
            beginAtomIdx=bond.get_atom1().get_id(),
            endAtomIdx=bond.get_atom2().get_id(),
            order=rdkit.BondType(bond.get_order())
        )

    edit_mol = edit_mol.GetMol()
    rdkit_conf = rdkit.Conformer(mol.get_num_atoms())
    for atom_id, atom_coord in enumerate(mol.get_position_matrix()):
        rdkit_conf.SetAtomPosition(atom_id, atom_coord)
        edit_mol.GetAtomWithIdx(atom_id).SetNoImplicit(True)
    edit_mol.AddConformer(rdkit_conf)

    return edit_mol
Пример #2
0
def remake(mol):
    """
    Remakes a molecule from scratch.

    Parameters
    ----------
    mol : :class:`rdkit.Mol`
        The molecule to be remade.

    Returns
    -------
    :class:`rdkit.Mol`
        The remade molecule.

    """

    emol = rdkit.EditableMol(rdkit.Mol())
    for atom in mol.GetAtoms():
        new = rdkit.Atom(atom.GetAtomicNum())
        new.SetFormalCharge(atom.GetFormalCharge())
        emol.AddAtom(new)

    for bond in mol.GetBonds():
        emol.AddBond(beginAtomIdx=bond.GetBeginAtomIdx(),
                     endAtomIdx=bond.GetEndAtomIdx(),
                     order=bond.GetBondType())

    m = emol.GetMol()
    m.AddConformer(mol.GetConformer())
    return m
Пример #3
0
    def to_rdkit_mol(self):
        """
        Return an :mod:`rdkit` representation.

        Returns
        -------
        :class:`rdkit.Mol`
            The molecule in :mod:`rdkit` format.

        """

        mol = rdkit.EditableMol(rdkit.Mol())
        for atom in self._atoms:
            rdkit_atom = rdkit.Atom(atom.get_atomic_number())
            rdkit_atom.SetFormalCharge(atom.get_charge())
            mol.AddAtom(rdkit_atom)

        for bond in self._bonds:
            mol.AddBond(
                beginAtomIdx=bond.get_atom1().get_id(),
                endAtomIdx=bond.get_atom2().get_id(),
                order=(rdkit.BondType.DATIVE if bond.get_order() == 9 else
                       rdkit.BondType(bond.get_order())),
            )

        mol = mol.GetMol()
        rdkit_conf = rdkit.Conformer(len(self._atoms))
        for atom_id, atom_coord in enumerate(self._position_matrix.T):
            rdkit_conf.SetAtomPosition(atom_id, atom_coord)
            mol.GetAtomWithIdx(atom_id).SetNoImplicit(True)
        mol.AddConformer(rdkit_conf)
        return mol
Пример #4
0
def remake_mol(mol):
    """
    Creates a copy of `mol` from scratch.

    This fixes f****d up valences which occur otherwise.

    Paramters
    ---------
    mol : :class:`rdkit.Chem.rdchem.Mol`
        The molecule to be copied.

    Returns
    -------
    class:`rdkit.Chem.rdchem.Mol`
        A copy of `mol`.

    """

    emol = rdkit.EditableMol(rdkit.Mol())
    for atom in mol.GetAtoms():
        emol.AddAtom(rdkit.Atom(atom.GetAtomicNum()))
    for bond in mol.GetBonds():
        emol.AddBond(bond.GetBeginAtomIdx(),
                     bond.GetEndAtomIdx(),
                     bond.GetBondType())
    new_mol = emol.GetMol()
    new_mol.AddConformer(rdkit.Conformer(mol.GetConformer()))
    return new_mol
Пример #5
0
    def to_rdkit_mol(self):
        """
        Return an :mod:`rdkit` representation.

        Returns
        -------
        :class:`rdkit.Mol`
            The molecule in :mod:`rdkit` format.

        """

        mol = rdkit.EditableMol(rdkit.Mol())
        for atom in self.atoms:
            rdkit_atom = rdkit.Atom(atom.atomic_number)
            rdkit_atom.SetFormalCharge(atom.charge)
            mol.AddAtom(rdkit_atom)

        for bond in self.bonds:
            mol.AddBond(beginAtomIdx=bond.atom1.id,
                        endAtomIdx=bond.atom2.id,
                        order=rdkit.BondType(bond.order))

        mol = mol.GetMol()
        rdkit_conf = rdkit.Conformer(len(self.atoms))
        for atom_id, atom_coord in enumerate(self._position_matrix.T):
            rdkit_conf.SetAtomPosition(atom_id, atom_coord)
        mol.AddConformer(rdkit_conf)
        return mol
Пример #6
0
 def add_atom(childGenes, GeneSet, oldGene):
     geneSet = GeneSet.Atoms
     newGeneNumber = childGenes.RWMol.GetNumAtoms()
     newGene = random.sample(geneSet, 1)[0]
     childGenes.RWMol.AddAtom(Chem.Atom(newGene))
     childGenes.RWMol.AddBond(newGeneNumber, oldGene, Chem.BondType.SINGLE)
     return childGenes
Пример #7
0
def get_rdkit_mol(molecule):
    rdkit_mol = rdkit.EditableMol(rdkit.Mol())
    for atom in molecule.get_atoms():
        rdkit_atom = rdkit.Atom(atom.get_atomic_number())
        rdkit_atom.SetFormalCharge(atom.get_charge())
        rdkit_mol.AddAtom(rdkit_atom)

    for bond in molecule.get_bonds():
        rdkit_mol.AddBond(
            beginAtomIdx=bond.get_atom1().get_id(),
            endAtomIdx=bond.get_atom2().get_id(),
            order=(rdkit.BondType.DATIVE if bond.get_order() == 9 else
                   rdkit.BondType(bond.get_order())),
        )
    return rdkit_mol.GetMol()
Пример #8
0
def with_hydrogens(molecule):
    rdkit_molecule = rdkit.EditableMol(rdkit.Mol())
    for atom in molecule.get_atoms():
        rdkit_atom = rdkit.Atom(atom.get_atomic_number())
        rdkit_atom.SetFormalCharge(atom.get_charge())
        rdkit_molecule.AddAtom(rdkit_atom)

    for bond in molecule.get_bonds():
        rdkit_molecule.AddBond(
            beginAtomIdx=bond.get_atom1_id(),
            endAtomIdx=bond.get_atom2_id(),
            order=rdkit.BondType(bond.get_order()),
        )
    rdkit_molecule = rdkit_molecule.GetMol()
    rdkit.SanitizeMol(rdkit_molecule)
    return rdkit.AddHs(rdkit_molecule)
Пример #9
0
def remake(mol):
    """
    Remakes a molecule from scratch.

    Parameters
    ----------
    mol : :class:`rdkit.Mol`
        The molecule to be remade.

    Returns
    -------
    :class:`rdkit.Mol`
        The remade molecule.

    """

    emol = rdkit.EditableMol(rdkit.Mol())
    for a in mol.GetAtoms():
        new_atom = rdkit.Atom(a.GetAtomicNum())
        # Set properties.
        for pname, pval in a.GetPropsAsDict(False, False).items():
            if isinstance(pval, int):
                new_atom.SetIntProp(pname, pval)
            elif isinstance(pval, bool):
                new_atom.SetBoolProp(pname, pval)
            else:
                new_atom.SetProp(pname, pval)

        new_atom.SetFormalCharge(a.GetFormalCharge())
        emol.AddAtom(new_atom)

    for bond in mol.GetBonds():
        emol.AddBond(bond.GetBeginAtomIdx(),
                     bond.GetEndAtomIdx(),
                     bond.GetBondType())

    m = emol.GetMol()
    m.AddConformer(rdkit.Conformer(mol.GetConformer()))

    for a in m.GetAtoms():
        a.UpdatePropertyCache()

    rdkit.GetSSSR(m)

    return m
Пример #10
0
def vabene_to_smiles(molecule):
    editable = rdkit.EditableMol(rdkit.Mol())
    for atom in molecule.get_atoms():
        rdkit_atom = rdkit.Atom(atom.get_atomic_number())
        rdkit_atom.SetFormalCharge(atom.get_charge())
        editable.AddAtom(rdkit_atom)

    for bond in molecule.get_bonds():
        editable.AddBond(
            beginAtomIdx=bond.get_atom1_id(),
            endAtomIdx=bond.get_atom2_id(),
            order=rdkit.BondType(bond.get_order()),
        )

    rdkit_molecule = editable.GetMol()
    rdkit.SanitizeMol(rdkit_molecule)
    rdkit.Kekulize(rdkit_molecule)
    return rdkit.MolToSmiles(rdkit_molecule)
Пример #11
0
def mol_from_mol_file(mol_file):
    """
    Creates a rdkit molecule from a ``.mol`` (V3000) file.

    Parameters
    ----------
    mol_file : :class:`str`
        The full of the .mol file from which an rdkit molecule should
        be instantiated.

    Returns
    -------
    :class:`rdkit.Mol`
        An rdkit instance of the molecule held in `mol2_file`.

    Raises
    ------
    :class:`ChargedMolError`
        If an atom row has more than 8 coloumns it is usually because
        there is a 9th coloumn indicating atomic charge. Such molecules
        are not currently supported, so an error is raised.

    :class:`MolFileError`
        If the file is not a V3000 ``.mol`` file.

    """

    e_mol = rdkit.EditableMol(rdkit.Mol())
    conf = rdkit.Conformer()

    with open(mol_file, 'r') as f:
        take_atom = False
        take_bond = False
        v3000 = False

        for line in f:
            if 'V3000' in line:
                v3000 = True

            if 'M  V30 BEGIN ATOM' in line:
                take_atom = True
                continue

            if 'M  V30 END ATOM' in line:
                take_atom = False
                continue

            if 'M  V30 BEGIN BOND' in line:
                take_bond = True
                continue

            if 'M  V30 END BOND' in line:
                take_bond = False
                continue

            if take_atom:
                words = line.split()
                if len(words) > 8:
                    raise ChargedMolError(mol_file,
                                          ('Atom row has more'
                                           ' than 8 coloumns. Likely '
                                           'due to a charged atom.'))
                _, _, _, atom_sym, *coords, _ = words
                coords = [float(x) for x in coords]
                atom_coord = Point3D(*coords)
                atom_id = e_mol.AddAtom(rdkit.Atom(atom_sym))
                conf.SetAtomPosition(atom_id, atom_coord)
                continue

            if take_bond:
                *_, bond_id,  bond_order, atom1, atom2 = line.split()
                e_mol.AddBond(int(atom1)-1, int(atom2)-1,
                              bond_dict[bond_order])
                continue
    if not v3000:
        raise MolFileError(mol_file, 'Not a V3000 .mol file.')

    mol = e_mol.GetMol()
    mol.AddConformer(conf)
    return mol
Пример #12
0
    def init_from_vabene_molecule(
        cls,
        molecule: vabene.Molecule,
        functional_groups: typing.Iterable[typing.Union[
            FunctionalGroup, FunctionalGroupFactory]] = (),
        placer_ids: typing.Optional[tuple[int, ...]] = None,
        position_matrix: typing.Optional[np.ndarray] = None,
    ) -> BuildingBlock:
        """
        Initialize from a :mod:`vabene.Molecule`.

        Notes:

            The molecule is given 3D coordinates with
            :func:`rdkit.ETKDGv2()`.

        Parameters:

            molecule:
                The :class:`vabene.Molecule` from which to initialize.

            functional_groups:
                An :class:`iterable` of :class:`.FunctionalGroup` or
                :class:`.FunctionalGroupFactory` or both.
                :class:`.FunctionalGroup` instances are added to the
                building block and :class:`.FunctionalGroupFactory`
                instances are used to create :class:`.FunctionalGroup`
                instances the building block should hold.
                :class:`.FunctionalGroup` instances are used to
                identify which atoms are modified during
                :class:`.ConstructedMolecule` construction.

            placer_ids:
                The ids of *placer* atoms. These are the atoms which
                should be used for calculating the position of the
                building block. Depending on the values passed to
                `placer_ids`, and the functional groups in the building
                block, different *placer* ids will be used by the
                building block.

                #. `placer_ids` is passed to the initializer: the
                   passed *placer* ids will be used by the building
                   block.

                #. `placer_ids` is ``None`` and the building block has
                   functional groups: The *placer* ids of the
                   functional groups will be used as the *placer* ids
                   of the building block.

                #. `placer_ids` is ``None`` and `functional_groups` is
                   empty. All atoms of the molecule will be used for
                   *placer* ids.

            position_matrix:
                The position matrix the building block should use. If
                ``None``, :func:`rdkit.ETKDGv2` will be used to
                calculate it.

        Returns:

             The building block.

        Raises:

            :class:`RuntimeError`
                If embedding the molecule fails.

        """

        editable = rdkit.EditableMol(rdkit.Mol())
        for atom in molecule.get_atoms():
            rdkit_atom = rdkit.Atom(atom.get_atomic_number())
            rdkit_atom.SetFormalCharge(atom.get_charge())
            editable.AddAtom(rdkit_atom)

        for bond in molecule.get_bonds():
            editable.AddBond(
                beginAtomIdx=bond.get_atom1_id(),
                endAtomIdx=bond.get_atom2_id(),
                order=rdkit.BondType(bond.get_order()),
            )

        rdkit_molecule = editable.GetMol()
        rdkit.SanitizeMol(rdkit_molecule)
        rdkit_molecule = rdkit.AddHs(rdkit_molecule)

        if position_matrix is None:
            params = rdkit.ETKDGv2()
            random_seed = 4
            params.randomSeed = random_seed
            if rdkit.EmbedMolecule(rdkit_molecule, params) == -1:
                raise RuntimeError(
                    f'Embedding with seed value of {random_seed} '
                    'failed.')
        else:
            # Make sure the position matrix always holds floats.
            position_matrix = np.array(
                position_matrix,
                dtype=np.float64,
            )
            conformer = rdkit.Conformer(rdkit_molecule.GetNumAtoms())
            for atom_id, position in enumerate(position_matrix):
                conformer.SetAtomPosition(atom_id, position)
            rdkit_molecule.AddConformer(conformer)

        rdkit.Kekulize(rdkit_molecule)
        return cls.init_from_rdkit_mol(
            molecule=rdkit_molecule,
            functional_groups=functional_groups,
            placer_ids=placer_ids,
        )
Пример #13
0
    def split(self, molecule):
        """
        Split a molecule into multiple molecules.

        Parameters
        ----------
        molecule : :class:`stk.Molecule`
            Molecule to split.

        Returns
        -------
        :class:`iterable` of :class:`stk.BuildingBlock`
            The resulting list of building blocks, with new functional
            groups assigned.

        """

        new_building_blocks = []

        rdkit_mol = molecule.to_rdkit_mol()
        rdkit.SanitizeMol(rdkit_mol)
        bond_pair_ids_to_delete = []
        reactable_atom_ids = []
        for atom_ids in rdkit_mol.GetSubstructMatches(
                query=rdkit.MolFromSmarts(self._breaker_smarts)):
            translated_bond_atom_ids = (
                atom_ids[self._bond_deleter_ids[0]],
                atom_ids[self._bond_deleter_ids[1]],
            )
            bond_pair_ids_to_delete.extend(
                tuple(sorted((i, j)))
                for i, j in combinations(translated_bond_atom_ids, 2))
            reactable_atom_ids.extend(i for i in translated_bond_atom_ids)

        bond_ids_to_delete = []
        for bond in rdkit_mol.GetBonds():
            idxs = tuple(sorted(
                (bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())))
            if idxs in bond_pair_ids_to_delete:
                bond_ids_to_delete.append(bond.GetIdx())

        # Delete bonds and atoms.
        fragments = rdkit.GetMolFrags(rdkit.FragmentOnBonds(
            mol=rdkit_mol,
            bondIndices=tuple(bond_ids_to_delete),
            addDummies=True,
        ),
                                      asMols=True)

        # Add replacers bonded to * atoms.
        for i in rdkit.MolFromSmarts(self._replacer_smarts).GetAtoms():
            replacer_atom = rdkit.Atom(i.GetAtomicNum())
            replacer_atom.SetFormalCharge(0)
            break

        for frag in fragments:
            editable = rdkit.EditableMol(frag)
            for atom in frag.GetAtoms():
                # 0 if dummy atom.
                if atom.GetAtomicNum() == 0:
                    editable.ReplaceAtom(atom.GetIdx(), replacer_atom)

            frag_rdkit = editable.GetMol()
            rdkit.Kekulize(frag_rdkit)
            new_building_blocks.append(
                stk.BuildingBlock.init_from_rdkit_mol(
                    molecule=frag_rdkit,
                    functional_groups=self._functional_groups))

        return new_building_blocks
Пример #14
0
def amine3_with_amine3(mol, del_atoms, fg1, fg2):
    """
    Crates bonds between functional groups.

    Parameters
    ----------
    mol : :class:`rdkit.Chem.rdchem.Mol`
        A molecule being assembled.

    del : :class:`bool`
        Toggles if atoms with the ``'del'`` property are deleted.

    fg1 : :class:`int`
        The id of the first functional group which
        is to be joined, as given by the 'fg_id' property.

    fg2 : :class:`int`
        The id of the second functional group which
        is to be joined, as given by the 'fg_id' property.

    Returns
    -------
    :class:`tuple`
        The first element is an :class:`rdkit.Chem.rdchem.Mol`. It is
        the molecule with bonds added between the functional groups.

        The second element is a :class:`int`. It is the number
        of bonds added.

    """

    fgs = {fg1, fg2}
    atoms1, atoms2 = {}, {}
    deleters = []

    for a in mol.GetAtoms():
        if not a.HasProp('fg_id') or a.GetIntProp('fg_id') not in fgs:
            continue

        if a.HasProp('bonder') and a.GetIntProp('fg_id') == fg1:
            atoms1[a.GetSymbol()] = a.GetIdx()

        if a.HasProp('bonder') and a.GetIntProp('fg_id') == fg2:
            atoms2[a.GetSymbol()] = a.GetIdx()

        if a.HasProp('del'):
            deleters.append(a.GetIdx())

    conf = mol.GetConformer()
    n1_pos = np.array([*conf.GetAtomPosition(atoms1['N'])])
    n2_pos = np.array([*conf.GetAtomPosition(atoms2['N'])])

    c1_pos = np.array([*conf.GetAtomPosition(atoms1['C'])])
    c2_pos = np.array([*conf.GetAtomPosition(atoms2['C'])])

    emol = rdkit.EditableMol(mol)

    n_joiner = emol.AddAtom(rdkit.Atom(6))
    n_joiner_pos = (n1_pos + n2_pos) / 2
    nh1 = emol.AddAtom(rdkit.Atom(1))
    nh1_pos = n_joiner_pos + np.array([0, 0, 1])
    nh2 = emol.AddAtom(rdkit.Atom(1))
    nh2_pos = n_joiner_pos + np.array([0, 0, -1])

    nc_joiner1 = emol.AddAtom(rdkit.Atom(6))
    nc_joiner1_pos = (c1_pos + n2_pos) / 2
    nc1h1 = emol.AddAtom(rdkit.Atom(1))
    nc1h1_pos = nc_joiner1_pos + np.array([0, 0, 1])
    nc1h2 = emol.AddAtom(rdkit.Atom(1))
    nc1h2_pos = nc_joiner1_pos + np.array([0, 0, -1])

    nc_joiner2 = emol.AddAtom(rdkit.Atom(6))
    nc_joiner2_pos = (c2_pos + n1_pos) / 2
    nc2h1 = emol.AddAtom(rdkit.Atom(1))
    nc2h1_pos = nc_joiner2_pos + np.array([0, 0, 1])
    nc2h2 = emol.AddAtom(rdkit.Atom(1))
    nc2h2_pos = nc_joiner2_pos + np.array([0, 0, -1])

    single = rdkit.rdchem.BondType.SINGLE
    emol.AddBond(atoms1['N'], n_joiner, single)
    emol.AddBond(atoms2['N'], n_joiner, single)
    emol.AddBond(n_joiner, nh1, single)
    emol.AddBond(n_joiner, nh2, single)

    emol.AddBond(atoms1['C'], nc_joiner1, single)
    emol.AddBond(atoms2['N'], nc_joiner1, single)
    emol.AddBond(nc_joiner1, nc1h1, single)
    emol.AddBond(nc_joiner1, nc1h2, single)

    emol.AddBond(atoms2['C'], nc_joiner2, single)
    emol.AddBond(atoms1['N'], nc_joiner2, single)
    emol.AddBond(nc_joiner2, nc2h1, single)
    emol.AddBond(nc_joiner2, nc2h2, single)

    mol = emol.GetMol()
    conf = mol.GetConformer()
    conf.SetAtomPosition(n_joiner, rdkit_geo.Point3D(*n_joiner_pos))
    conf.SetAtomPosition(nh1, rdkit_geo.Point3D(*nh1_pos))
    conf.SetAtomPosition(nh2, rdkit_geo.Point3D(*nh2_pos))

    conf.SetAtomPosition(nc_joiner1, rdkit_geo.Point3D(*nc_joiner1_pos))
    conf.SetAtomPosition(nc1h1, rdkit_geo.Point3D(*nc1h1_pos))
    conf.SetAtomPosition(nc1h2, rdkit_geo.Point3D(*nc1h2_pos))

    conf.SetAtomPosition(nc_joiner2, rdkit_geo.Point3D(*nc_joiner2_pos))
    conf.SetAtomPosition(nc2h1, rdkit_geo.Point3D(*nc2h1_pos))
    conf.SetAtomPosition(nc2h2, rdkit_geo.Point3D(*nc2h2_pos))

    if del_atoms:
        emol = rdkit.EditableMol(mol)
        for a in reversed(deleters):
            emol.RemoveAtom(a)
        mol = emol.GetMol()

    return mol, 6
Пример #15
0
    def ring_amine_with_ring_amine(self, fg1, fg2):
        """
        Creates bonds between functional groups.

        Parameters
        ----------
        fg1 : :class:`.FunctionalGroup`
            A functional group which undergoes the reaction.

        fg2 : :class:`.FunctionalGroup`
            A functional group which undergoes the reaction.

        Returns
        -------
        None : :class:`NoneType`

        """

        c1 = next(a for a in fg1.bonder_ids
                  if self.mol.GetAtomWithIdx(a).GetAtomicNum() == 6)
        n1 = next(a for a in fg1.bonder_ids
                  if self.mol.GetAtomWithIdx(a).GetAtomicNum() == 7)

        c2 = next(a for a in fg2.bonder_ids
                  if self.mol.GetAtomWithIdx(a).GetAtomicNum() == 6)
        n2 = next(a for a in fg2.bonder_ids
                  if self.mol.GetAtomWithIdx(a).GetAtomicNum() == 7)

        conf = self.mol.GetConformer()
        n1_pos = np.array([*conf.GetAtomPosition(n1)])
        n2_pos = np.array([*conf.GetAtomPosition(n2)])

        c1_pos = np.array([*conf.GetAtomPosition(c1)])
        c2_pos = np.array([*conf.GetAtomPosition(c2)])

        n_joiner = self.emol.AddAtom(rdkit.Atom(6))
        n_joiner_pos = (n1_pos + n2_pos) / 2
        self.new_atom_coords.append((n_joiner, n_joiner_pos))

        nh1 = self.emol.AddAtom(rdkit.Atom(1))
        nh1_pos = n_joiner_pos + np.array([0, 0, 1])
        self.new_atom_coords.append((nh1, nh1_pos))

        nh2 = self.emol.AddAtom(rdkit.Atom(1))
        nh2_pos = n_joiner_pos + np.array([0, 0, -1])
        self.new_atom_coords.append((nh2, nh2_pos))

        nc_joiner1 = self.emol.AddAtom(rdkit.Atom(6))
        nc_joiner1_pos = (c1_pos + n2_pos) / 2
        self.new_atom_coords.append((nc_joiner1, nc_joiner1_pos))

        nc1h1 = self.emol.AddAtom(rdkit.Atom(1))
        nc1h1_pos = nc_joiner1_pos + np.array([0, 0, 1])
        self.new_atom_coords.append((nc1h1, nc1h1_pos))

        nc1h2 = self.emol.AddAtom(rdkit.Atom(1))
        nc1h2_pos = nc_joiner1_pos + np.array([0, 0, -1])
        self.new_atom_coords.append((nc1h2, nc1h2_pos))

        nc_joiner2 = self.emol.AddAtom(rdkit.Atom(6))
        nc_joiner2_pos = (c2_pos + n1_pos) / 2
        self.new_atom_coords.append((nc_joiner2, nc_joiner2_pos))

        nc2h1 = self.emol.AddAtom(rdkit.Atom(1))
        nc2h1_pos = nc_joiner2_pos + np.array([0, 0, 1])
        self.new_atom_coords.append((nc2h1, nc2h1_pos))

        nc2h2 = self.emol.AddAtom(rdkit.Atom(1))
        nc2h2_pos = nc_joiner2_pos + np.array([0, 0, -1])
        self.new_atom_coords.append((nc2h2, nc2h2_pos))

        single = rdkit.rdchem.BondType.SINGLE
        self.emol.AddBond(n1, n_joiner, single)
        self.emol.AddBond(n2, n_joiner, single)
        self.emol.AddBond(n_joiner, nh1, single)
        self.emol.AddBond(n_joiner, nh2, single)

        self.emol.AddBond(c1, nc_joiner1, single)
        self.emol.AddBond(n2, nc_joiner1, single)
        self.emol.AddBond(nc_joiner1, nc1h1, single)
        self.emol.AddBond(nc_joiner1, nc1h2, single)

        self.emol.AddBond(c2, nc_joiner2, single)
        self.emol.AddBond(n1, nc_joiner2, single)
        self.emol.AddBond(nc_joiner2, nc2h1, single)
        self.emol.AddBond(nc_joiner2, nc2h2, single)

        self.bonds_made += 6
Пример #16
0
def convert_ob_mol_to_rd_mol(ob_mol, struct=None):
    '''Convert OBMol to RDKit mol, fixing up issues'''
    ob_mol.DeleteHydrogens()
    n_atoms = ob_mol.NumAtoms()
    rd_mol = Chem.RWMol()
    rd_conf = Chem.Conformer(n_atoms)

    for ob_atom in ob.OBMolAtomIter(ob_mol):
        rd_atom = Chem.Atom(ob_atom.GetAtomicNum())
        #TODO copy format charge
        if ob_atom.IsAromatic() and ob_atom.IsInRing(
        ) and ob_atom.MemberOfRingSize() <= 6:
            #don't commit to being aromatic unless rdkit will be okay with the ring status
            #(this can happen if the atoms aren't fit well enough)
            rd_atom.SetIsAromatic(True)
        i = rd_mol.AddAtom(rd_atom)
        ob_coords = ob_atom.GetVector()
        x = ob_coords.GetX()
        y = ob_coords.GetY()
        z = ob_coords.GetZ()
        rd_coords = Geometry.Point3D(x, y, z)
        rd_conf.SetAtomPosition(i, rd_coords)

    rd_mol.AddConformer(rd_conf)

    for ob_bond in ob.OBMolBondIter(ob_mol):
        i = ob_bond.GetBeginAtomIdx() - 1
        j = ob_bond.GetEndAtomIdx() - 1
        bond_order = ob_bond.GetBondOrder()
        if bond_order == 1:
            rd_mol.AddBond(i, j, Chem.BondType.SINGLE)
        elif bond_order == 2:
            rd_mol.AddBond(i, j, Chem.BondType.DOUBLE)
        elif bond_order == 3:
            rd_mol.AddBond(i, j, Chem.BondType.TRIPLE)
        else:
            raise Exception('unknown bond order {}'.format(bond_order))

        if ob_bond.IsAromatic():
            bond = rd_mol.GetBondBetweenAtoms(i, j)
            bond.SetIsAromatic(True)

    rd_mol = Chem.RemoveHs(rd_mol, sanitize=False)

    pt = Chem.GetPeriodicTable()
    #if double/triple bonds are connected to hypervalent atoms, decrement the order

    positions = rd_mol.GetConformer().GetPositions()
    nonsingles = []
    for bond in rd_mol.GetBonds():
        if bond.GetBondType() == Chem.BondType.DOUBLE or bond.GetBondType(
        ) == Chem.BondType.TRIPLE:
            i = bond.GetBeginAtomIdx()
            j = bond.GetEndAtomIdx()
            dist = np.linalg.norm(positions[i] - positions[j])
            nonsingles.append((dist, bond))
    nonsingles.sort(reverse=True, key=lambda t: t[0])

    for (d, bond) in nonsingles:
        a1 = bond.GetBeginAtom()
        a2 = bond.GetEndAtom()

        if calc_valence(a1) > pt.GetDefaultValence(a1.GetAtomicNum()) or \
           calc_valence(a2) > pt.GetDefaultValence(a2.GetAtomicNum()):
            btype = Chem.BondType.SINGLE
            if bond.GetBondType() == Chem.BondType.TRIPLE:
                btype = Chem.BondType.DOUBLE
            bond.SetBondType(btype)

    for atom in rd_mol.GetAtoms():
        #set nitrogens with 4 neighbors to have a charge
        if atom.GetAtomicNum() == 7 and atom.GetDegree() == 4:
            atom.SetFormalCharge(1)

    rd_mol = Chem.AddHs(rd_mol, addCoords=True)

    positions = rd_mol.GetConformer().GetPositions()
    center = np.mean(positions[np.all(np.isfinite(positions), axis=1)], axis=0)
    for atom in rd_mol.GetAtoms():
        i = atom.GetIdx()
        pos = positions[i]
        if not np.all(np.isfinite(pos)):
            #hydrogens on C fragment get set to nan (shouldn't, but they do)
            rd_mol.GetConformer().SetAtomPosition(i, center)

    try:
        Chem.SanitizeMol(rd_mol, Chem.SANITIZE_ALL ^ Chem.SANITIZE_KEKULIZE)
    except:  # mtr22 - don't assume mols will pass this
        pass
        # dkoes - but we want to make failures as rare as possible and should debug them
        m = pybel.Molecule(ob_mol)
        i = np.random.randint(1000000)
        outname = 'bad%d.sdf' % i
        print("WRITING", outname)
        m.write('sdf', outname, overwrite=True)
        pickle.dump(struct, open('bad%d.pkl' % i, 'wb'))

    #but at some point stop trying to enforce our aromaticity -
    #openbabel and rdkit have different aromaticity models so they
    #won't always agree.  Remove any aromatic bonds to non-aromatic atoms
    for bond in rd_mol.GetBonds():
        a1 = bond.GetBeginAtom()
        a2 = bond.GetEndAtom()
        if bond.GetIsAromatic():
            if not a1.GetIsAromatic() or not a2.GetIsAromatic():
                bond.SetIsAromatic(False)
        elif a1.GetIsAromatic() and a2.GetIsAromatic():
            bond.SetIsAromatic(True)

    return rd_mol
Пример #17
0
mol_atm_idxs_list = list()
input_mol_frags = Chem.FragmentOnBonds(input_mol,
                                       bond_list,
                                       dummyLabels=dummy_labels)
flist = list()
for fragmol, fragmol_atm_idxs in zip(
        Chem.GetMolFrags(input_mol_frags, asMols=True),
        Chem.GetMolFrags(input_mol_frags, asMols=False)):
    flist.append(fragmol)
    atm_idx_list = list()
    rwmol = Chem.RWMol(fragmol)
    for atm, atm_idx in zip(fragmol.GetAtoms(), fragmol_atm_idxs):
        ### This is where the labels are stored
        label = atm.GetIsotope()
        if label == 1:
            rwmol.ReplaceAtom(atm.GetIdx(), Chem.Atom(8))
            rwmol.AddAtom(Chem.Atom(6))
            rwmol.AddBond(atm.GetIdx(),
                          fragmol.GetNumAtoms(),
                          order=Chem.rdchem.BondType.SINGLE)
            fragmol = rwmol.GetMol()
        elif label == 4:
            rwmol.ReplaceAtom(atm.GetIdx(), Chem.Atom(6))
        else:
            atm_idx_list.append(atm_idx)
    fragmol = rwmol.GetMol()
    Chem.SanitizeMol(fragmol)
    fragmol.Compute2DCoords()
    mol_list.append(fragmol)
    mol_atm_idxs_list.append(atm_idx_list)
Пример #18
0
def extract_core_from_mol(mol, reactive_atoms):
    """ Marks and removes non-core atoms from the reactant molecules and returns the reactive part of the reactants or
        the isolated reaction core for the products. """

    # Create an editable RDKit RWMol object and create a backup copy of it for later use.
    editable_mol = AllChem.RWMol(mol)
    default_editable_mol = deepcopy(editable_mol)

    # If the indices are not correct or an empty array, return the starting molecule.
    if len(reactive_atoms) == len(mol.GetAtoms()) or len(reactive_atoms) == 0:
        return editable_mol, default_editable_mol

    # Create the list of atoms that are not part of the core and sort them in DESC order to avoid removal conflicts.
    nr_atoms = sorted([
        atom.GetIdx()
        for atom in mol.GetAtoms() if atom.GetIdx() not in reactive_atoms
    ],
                      reverse=True)

    # First, try to remove molecule atoms that are marked as non-reactive.
    try:
        for rm_atom in nr_atoms:
            editable_mol.ReplaceAtom(rm_atom, AllChem.Atom("*"))

        # Save the state with the wildcard atoms for later use in fragmentation of reactants.
        basic_editable_mol = deepcopy(editable_mol)
        # Remove all bonds that were replaced with the wildcard atom '*'.
        remove_marked_bonds(editable_mol)

    # If this fails, usually due to sanitization errors, remove molecule atoms according to the expanded core.Some cores
    # are complete aromatic rings or fused rings which cannot be broken and converted to a RDKit Mol object. Expand the
    # core indices to include all rings or fused rings, and generate a list of atoms that are not part of this core.
    except:
        # Generate the expanded core.
        expanded_core = fetch_rest_of_ring_atoms(mol, reactive_atoms)
        expanded_synthon_atoms = sorted([
            atom.GetIdx()
            for atom in mol.GetAtoms() if atom.GetIdx() not in expanded_core
        ],
                                        reverse=True)

        # Create a new copy of the molecule because the previous one may have been modified.
        editable_mol = deepcopy(default_editable_mol)

        # To prevent later sanitization errors due to incorrect valences, skip any isolated atoms connected to the core.
        skip_atoms = []
        for bond in editable_mol.GetBonds():
            if bond.GetBeginAtomIdx() in expanded_core and bond.GetEndAtomIdx() not in expanded_core and \
                    editable_mol.GetAtomWithIdx(bond.GetEndAtomIdx()).GetDegree() == 1:
                skip_atoms.append(bond.GetEndAtomIdx())
            if bond.GetEndAtomIdx() in expanded_core and bond.GetBeginAtomIdx() not in expanded_core and \
                    editable_mol.GetAtomWithIdx(bond.GetBeginAtomIdx()).GetDegree() == 1:
                skip_atoms.append(bond.GetBeginAtomIdx())

        # Replace all of the atoms with the wildcard atom symbol '*'.
        for atom in set(expanded_synthon_atoms).difference(set(skip_atoms)):
            editable_mol.ReplaceAtom(atom, AllChem.Atom("*"))

        # Save the state with the wildcard atoms for later use in fragmentation of reactants.
        basic_editable_mol = deepcopy(editable_mol)
        # Remove all bonds that were replaced with the wildcard atom '*'.
        remove_marked_bonds(editable_mol)

    # Return the editable RDKit RWMol object after the changes have been made.
    return editable_mol, basic_editable_mol
Пример #19
0
def mol_from_mae_file(mae_path):
    """
    Creates a ``rdkit`` molecule from a ``.mae`` file.

    Parameters
    ----------
    mol2_file : :class:`str`
        The full path of the ``.mae`` file from which an rdkit molecule
        should be instantiated.

    Returns
    -------
    :class:`rdkit.Mol`
        An ``rdkit`` instance of the molecule held in `mae_file`.

    """

    mol = rdkit.EditableMol(rdkit.Mol())
    conf = rdkit.Conformer()

    with open(mae_path, 'r') as mae:
        content = re.split(r'[{}]', mae.read())

    prev_block = deque([''], maxlen=1)
    for block in content:
        if 'm_atom[' in prev_block[0]:
            atom_block = block
        if 'm_bond[' in prev_block[0]:
            bond_block = block
        prev_block.append(block)

    labels, data_block, *_ = atom_block.split(':::')
    labels = [
        label for label in labels.split('\n')
        if not label.isspace() and label != ''
    ]

    data_block = [
        a.split() for a in data_block.split('\n')
        if not a.isspace() and a != ''
    ]

    for line in data_block:
        line = [word for word in line if word != '"']
        if len(labels) != len(line):
            raise RuntimeError(('Number of labels does'
                                ' not match number of columns'
                                ' in .mae file.'))

        for label, data in zip(labels, line):
            if 'x_coord' in label:
                x = float(data)
            if 'y_coord' in label:
                y = float(data)
            if 'z_coord' in label:
                z = float(data)
            if 'atomic_number' in label:
                atom_num = int(data)

        atom_sym = periodic_table[atom_num]
        atom_coord = Point3D(x, y, z)
        atom_id = mol.AddAtom(rdkit.Atom(atom_sym))
        conf.SetAtomPosition(atom_id, atom_coord)

    labels, data_block, *_ = bond_block.split(':::')
    labels = [
        label for label in labels.split('\n')
        if not label.isspace() and label != ''
    ]
    data_block = [
        a.split() for a in data_block.split('\n')
        if not a.isspace() and a != ''
    ]

    for line in data_block:
        if len(labels) != len(line):
            raise RuntimeError(('Number of labels does'
                                ' not match number of '
                                'columns in .mae file.'))

        for label, data in zip(labels, line):
            if 'from' in label:
                atom1 = int(data) - 1
            if 'to' in label:
                atom2 = int(data) - 1
            if 'order' in label:
                bond_order = str(int(data))
        mol.AddBond(atom1, atom2, bond_dict[bond_order])

    mol = mol.GetMol()
    mol.AddConformer(conf)
    return mol
Пример #20
0
def extract_synthons_from_product(product_mol, reactive_atoms):
    """  Marks and removes the reactive atoms from the product molecules and returns only synthon templates. """

    # Create an editable RDKit RWMol object and create a backup copy of it for later use.
    editable_mol = AllChem.RWMol(product_mol)
    default_editable_mol = deepcopy(editable_mol)

    # If the indices are not correct or an empty array, return the starting molecule.
    if len(reactive_atoms) == len(
            product_mol.GetAtoms()) or len(reactive_atoms) == 0:
        return editable_mol

    # First check if all of the core bonds are aromatic.
    if all([
            str(bond.GetBondType()) == "AROMATIC"
            for bond in editable_mol.GetBonds()
            if bond.GetBeginAtomIdx() in reactive_atoms
            and bond.GetEndAtomIdx() in reactive_atoms
    ]):
        # ------------------------------------------------
        # Reactive atoms CONTAIN ONLY FULL AROMATIC RINGS.
        # ------------------------------------------------
        if atoms_contain_complete_rings(editable_mol, reactive_atoms):
            # Try removing all aromatic atoms in the core ring, except atoms shared between fused rings.
            try:
                for bond in editable_mol.GetBonds():
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) in reactive_atoms:
                        if count_atom_ring_memberships(
                                editable_mol, bond.GetBeginAtomIdx()) < 2:
                            editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                     AllChem.Atom("*"))
                        if count_atom_ring_memberships(
                                editable_mol, bond.GetEndAtomIdx()) < 2:
                            editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                     AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

            # If this fails, it's mostly due to kekulization issues. Remove all non-aromatic atoms attached to the ring.
            except:
                try:
                    # Create a new copy of the molecule because the previous one may have been modified.
                    editable_mol = deepcopy(default_editable_mol)

                    for bond in editable_mol.GetBonds():
                        if str(bond.GetBondType()) != "AROMATIC":
                            if bond.GetBeginAtomIdx(
                            ) in reactive_atoms and bond.GetEndAtomIdx(
                            ) not in reactive_atoms:
                                editable_mol.ReplaceAtom(
                                    bond.GetBeginAtomIdx(), AllChem.Atom("*"))
                                editable_mol.ReplaceAtom(
                                    bond.GetEndAtomIdx(), AllChem.Atom("*"))
                            elif bond.GetEndAtomIdx() in reactive_atoms and \
                                    bond.GetBeginAtomIdx() not in reactive_atoms:
                                editable_mol.ReplaceAtom(
                                    bond.GetBeginAtomIdx(), AllChem.Atom("*"))
                                editable_mol.ReplaceAtom(
                                    bond.GetEndAtomIdx(), AllChem.Atom("*"))

                    # Remove all bonds that were replaced with the wildcard atom '*'.
                    remove_marked_bonds(editable_mol)

                # If that also fails, completely remove all aromatic atoms from the ring.
                except:
                    # Create a new copy of the molecule because the previous one may have been modified.
                    editable_mol = deepcopy(default_editable_mol)

                    for bond in editable_mol.GetBonds():
                        if str(bond.GetBondType()) == "AROMATIC":
                            if bond.GetBeginAtomIdx(
                            ) in reactive_atoms and bond.GetEndAtomIdx(
                            ) in reactive_atoms:
                                editable_mol.ReplaceAtom(
                                    bond.GetBeginAtomIdx(), AllChem.Atom("*"))
                                editable_mol.ReplaceAtom(
                                    bond.GetEndAtomIdx(), AllChem.Atom("*"))

                    # Remove all bonds that were replaced with the wildcard atom '*'.
                    remove_marked_bonds(editable_mol)

        # ------------------------------------------------------------------
        # Reactive atoms CONTAIN ALL AROMATIC BONDS, BUT NOT COMPLETE RINGS.
        # ------------------------------------------------------------------
        else:
            # Try removing all of the atoms normally.
            try:
                for rm_atom in reactive_atoms:
                    editable_mol.ReplaceAtom(rm_atom, AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

            # If this fails, mark and remove only the nearest non-aromatic bond connected to the aromatic part.
            except:
                # Create a new copy of the molecule because the previous one may have been modified.
                editable_mol = deepcopy(default_editable_mol)

                for bond in editable_mol.GetBonds():
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) in reactive_atoms:
                        if str(bond.GetBondType()) != "AROMATIC":
                            editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                     AllChem.Atom("*"))
                            editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                     AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

    # -----------------------------------------
    # Reactive atoms CONTAIN NO AROMATIC BONDS.
    # -----------------------------------------
    elif not any([
            str(bond.GetBondType()) == "AROMATIC"
            for bond in editable_mol.GetBonds() if bond.GetBeginAtomIdx() in
            reactive_atoms and bond.GetEndAtomIdx() in reactive_atoms
    ]):
        # Try removing all of the atoms normally. This should work for all cases.
        for rm_atom in reactive_atoms:
            editable_mol.ReplaceAtom(rm_atom, AllChem.Atom("*"))

        # Remove all bonds that were replaced with the wildcard atom '*'.
        remove_marked_bonds(editable_mol)

    # -------------------------------------------------------------
    # Reactive atoms CONTAIN COMPLETE RINGS AND NON-AROMATIC BONDS.
    # -------------------------------------------------------------
    else:
        if atoms_contain_complete_rings(editable_mol, reactive_atoms):
            # Try removing all aromatic atoms in the core ring, except atoms shared between fused rings.
            try:
                for bond in editable_mol.GetBonds():
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) in reactive_atoms:
                        if count_atom_ring_memberships(
                                editable_mol, bond.GetBeginAtomIdx()) < 2:
                            editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                     AllChem.Atom("*"))
                        if count_atom_ring_memberships(
                                editable_mol, bond.GetEndAtomIdx()) < 2:
                            editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                     AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

            # If this fails, it's mostly due to kekulization issues. Remove all non-aromatic atoms attached to the ring.
            except:
                # Create a new copy of the molecule because the previous one may have been modified.
                editable_mol = deepcopy(default_editable_mol)

                for bond in editable_mol.GetBonds():
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) in reactive_atoms:
                        if str(bond.GetBondType()) != "AROMATIC":
                            editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                     AllChem.Atom("*"))
                            editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                     AllChem.Atom("*"))

                # Sanitize the molecule at this point because some of these entries require it here.
                AllChem.SanitizeMol(editable_mol)
                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

        # ------------------------------------------------------------------------------
        # Reactive atoms CONTAIN AROMATIC AND NON-AROMATIC BONDS, BUT NO COMPLETE RINGS.
        # ------------------------------------------------------------------------------
        else:
            try:
                # Try removing all of the atoms normally. This should work for all cases.
                for rm_atom in reactive_atoms:
                    editable_mol.ReplaceAtom(rm_atom, AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                remove_marked_bonds(editable_mol)

            # If this fails, remove only the non-aromatic bonds.
            except:
                # Create a new copy of the molecule because the previous one may have been modified.
                editable_mol = deepcopy(default_editable_mol)

                for bond in editable_mol.GetBonds():
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) in reactive_atoms:
                        if str(bond.GetBondType()) != "AROMATIC":
                            editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                     AllChem.Atom("*"))
                            editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                     AllChem.Atom("*"))

                # Remove all bonds that were replaced with the wildcard atom '*'.
                # If this also fails because of wrongly marked AROMATIC, return default molecule.
                try:
                    remove_marked_bonds(editable_mol)
                except:
                    editable_mol = deepcopy(default_editable_mol)

    # Return the editable RDKit RWMol object after the changes have been made.
    return editable_mol
Пример #21
0
def extract_synthons_from_reactant(reactant_mol, reactive_atoms):
    """ Marks and removes marked reactive atoms from the reactant molecules and returns only the non-reactive part. """

    # Create an editable RDKit RWMol object and create a backup copy of it for later use.
    editable_mol = AllChem.RWMol(reactant_mol)
    default_editable_mol = deepcopy(editable_mol)

    # If the indices are not correct or an empty array, return the starting molecule.
    if len(reactive_atoms) == len(
            reactant_mol.GetAtoms()) or len(reactive_atoms) == 0:
        return editable_mol, default_editable_mol

    # First, try to just remove all of the core atoms without any further modifications.
    try:
        for rm_atom in reactive_atoms:
            editable_mol.ReplaceAtom(rm_atom, AllChem.Atom("*"))

        # Save the state with the wildcard atoms for later use in fragmentation of reactants.
        basic_editable_mol = deepcopy(editable_mol)
        # Remove all bonds that were replaced with the wildcard atom '*'.
        remove_marked_bonds(editable_mol)

    # If that fails, it's most likely due to the incorrect decomposition of aromatic rings.
    except:
        # Create a new copy of the molecule because the previous one may have been modified.
        editable_mol = deepcopy(default_editable_mol)

        # Try removing all atoms in the present aromatic ring, except atoms shared between fused rings.
        try:
            for bond in editable_mol.GetBonds():
                if bond.GetBeginAtomIdx(
                ) in reactive_atoms and bond.GetEndAtomIdx() in reactive_atoms:
                    if count_atom_ring_memberships(editable_mol,
                                                   bond.GetBeginAtomIdx()) < 2:
                        editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                 AllChem.Atom("*"))
                    if count_atom_ring_memberships(editable_mol,
                                                   bond.GetEndAtomIdx()) < 2:
                        editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                 AllChem.Atom("*"))

            # Save the state with the wildcard atoms for later use in fragmentation of reactants.
            basic_editable_mol = deepcopy(editable_mol)
            # Remove all bonds that were replaced with the wildcard atom '*'.
            remove_marked_bonds(editable_mol)

        # If this also fails, only remove non-aromatic bond atoms attached to the ring if there are any.
        except:
            # Create a new copy of the molecule because the previous one may have been modified.
            editable_mol = deepcopy(default_editable_mol)

            for bond in editable_mol.GetBonds():
                if str(bond.GetBondType()) != "AROMATIC":
                    if bond.GetBeginAtomIdx(
                    ) in reactive_atoms and bond.GetEndAtomIdx(
                    ) not in reactive_atoms:
                        editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                 AllChem.Atom("*"))
                        editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                 AllChem.Atom("*"))
                    elif bond.GetEndAtomIdx(
                    ) in reactive_atoms and bond.GetBeginAtomIdx(
                    ) not in reactive_atoms:
                        editable_mol.ReplaceAtom(bond.GetBeginAtomIdx(),
                                                 AllChem.Atom("*"))
                        editable_mol.ReplaceAtom(bond.GetEndAtomIdx(),
                                                 AllChem.Atom("*"))

            # Save the state with the wildcard atoms for later use in fragmentation of reactants.
            basic_editable_mol = deepcopy(editable_mol)
            # Remove all bonds that were replaced with the wildcard atom '*'.
            remove_marked_bonds(editable_mol)

    # Return the editable RDKit RWMol object after the changes have been made.
    return editable_mol, basic_editable_mol
Пример #22
0
    def init_from_vabene_molecule(
        cls,
        molecule,
        functional_groups=(),
        placer_ids=None,
        random_seed=None,
        position_matrix=None,
    ):
        """
        Initialize from a :mod:`vabene.Molecule`.

        Notes
        -----
        The molecule is given 3D coordinates with
        :func:`rdkit.ETKDGv2()`.

        Parameters
        ----------
        molecule : :class:`vabene.Molecule`
            The molecule from which to initialize.

        functional_groups : :class:`iterable`, optional
            An :class:`iterable` of :class:`.FunctionalGroup` or
            :class:`.FunctionalGroupFactory` or both.
            :class:`.FunctionalGroup` instances are added to the
            building block and :class:`.FunctionalGroupFactory`
            instances are used to create :class:`.FunctionalGroup`
            instances the building block should hold.
            :class:`.FunctionalGroup` instances are used to identify
            which atoms are modified during
            :class:`.ConstructedMolecule` construction.

        placer_ids : :class:`tuple` of :class:`int`, optional
            The ids of *placer* atoms. These are the atoms which should
            be used for calculating the position of the building block.
            Depending on the values passed to `placer_ids`,
            and the functional groups in the building block, different
            *placer* ids will be used by the building block.

            #. `placer_ids` is passed to the initializer: the passed
               *placer* ids will be used by the building block.

            #. `placer_ids` is ``None`` and the building block has
               functional groups: The *placer* ids of the functional
               groups will be used as the *placer* ids of the building
               block.

            #. `placer_ids` is ``None`` and `functional_groups` is
               empty. All atoms of the molecule will be used for
               *placer* ids.

        random_seed : :class:`int`, optional
            Random seed passed to :func:`rdkit.ETKDGv2`. This
            argument is deprecated and will be removed in any
            version of :mod:`stk` released on, or after, 01/08/20.
            If you want to change the position matrix of the
            initialized building block, please use
            `position_matrix`.

        position_matrix : :class:`numpy.ndarray`, optional
            The position matrix the building block should use. If
            ``None``, :func:`rdkit.ETKDGv2` will be used to calculate
            it.

        Returns
        -------
        :class:`.BuildingBlock`
             The building block.

        Raises
        ------
        :class:`RuntimeError`
            If embedding the molecule fails.

        """

        if random_seed is not None:
            warnings.warn(
                'The init_from_vabene_molecule random seed parameter '
                'will be removed in any '
                'version of stk released on, or after, 01/08/20. '
                'If you want to change the position matrix of the '
                'initialized building block, please use the '
                'position_matrix parameter.',
                FutureWarning,
            )
        else:
            random_seed = 4

        editable = rdkit.EditableMol(rdkit.Mol())
        for atom in molecule.get_atoms():
            rdkit_atom = rdkit.Atom(atom.get_atomic_number())
            rdkit_atom.SetFormalCharge(atom.get_charge())
            editable.AddAtom(rdkit_atom)

        for bond in molecule.get_bonds():
            editable.AddBond(
                beginAtomIdx=bond.get_atom1_id(),
                endAtomIdx=bond.get_atom2_id(),
                order=rdkit.BondType(bond.get_order()),
            )

        rdkit_molecule = editable.GetMol()
        rdkit.SanitizeMol(rdkit_molecule)
        rdkit_molecule = rdkit.AddHs(rdkit_molecule)

        if position_matrix is None:
            params = rdkit.ETKDGv2()
            params.randomSeed = random_seed
            if rdkit.EmbedMolecule(rdkit_molecule, params) == -1:
                raise RuntimeError(
                    f'Embedding with seed value of {random_seed} '
                    'failed.'
                )
        else:
            # Make sure the position matrix always holds floats.
            position_matrix = np.array(
                position_matrix,
                dtype=np.float64,
            )
            conformer = rdkit.Conformer(rdkit_molecule.GetNumAtoms())
            for atom_id, position in enumerate(position_matrix):
                conformer.SetAtomPosition(atom_id, position)
            rdkit_molecule.AddConformer(conformer)

        rdkit.Kekulize(rdkit_molecule)
        return cls.init_from_rdkit_mol(
            molecule=rdkit_molecule,
            functional_groups=functional_groups,
            placer_ids=placer_ids,
        )