def atom_bond_set_to_mol(frag, oemol, adjust_hcount=False, RGroup=True):
    from openeye import oechem
    import warnings
    fragatompred = oechem.OEIsAtomMember(frag.GetAtoms())
    fragbondpred = oechem.OEIsBondMember(frag.GetBonds())

    fragment_oemol = oechem.OEMol()
    adjustHCount = adjust_hcount
    oechem.OESubsetMol(fragment_oemol, oemol, fragatompred, fragbondpred,
                       adjustHCount, RGroup)

    oechem.OEAddExplicitHydrogens(fragment_oemol)
    # sanity check that all atoms are bonded
    for atom in fragment_oemol.GetAtoms():
        if not list(atom.GetBonds()):
            warnings.warn(
                "Yikes!!! An atom that is not bonded to any other atom in the fragment. "
                "You probably ran into a bug. Please report the input molecule to the issue tracker"
            )
    # Perceive stereo and check that defined stereo did not change
    oechem.OEPerceiveChiral(fragment_oemol)
    oechem.OE3DToAtomStereo(fragment_oemol)
    oechem.OE3DToBondStereo(fragment_oemol)

    return fragment_oemol
Exemple #2
0
def file_to_oemols(filename):
    """Create OEMol from file. If more than one mol in file, return list of OEMols.

    Parameters
    ----------
    filename: str
        absolute path to oeb file

    Returns
    -------
    mollist: list
        list of OEMol for multiple molecules. OEMol if file only has one molecule.
    """
    from openeye import oechem

    if not os.path.exists(filename):
        raise Exception("File {} not found".format(filename))

    ifs = oechem.oemolistream(filename)
    mollist = []

    molecule = oechem.OEMol()
    while oechem.OEReadMolecule(ifs, molecule):
        molecule_copy = oechem.OEMol(molecule)
        oechem.OEPerceiveChiral(molecule_copy)
        oechem.OE3DToAtomStereo(molecule_copy)
        oechem.OE3DToBondStereo(molecule_copy)
        mollist.append(molecule_copy)
    ifs.close()

    if len(mollist) is 1:
        mollist = mollist[0]
    return mollist
Exemple #3
0
def mol_from_json(symbols, connectivity, geometry, permute_xyz=False):
    """
    Generate OEMol from QCSchema molecule specs
    Parameters
    ----------
    inp_molecule: dict
        Must have symbols and connectivity and/or geometry
        Note: If geometry is given, the molecule will have a tag indicating that the goemetry came from QCSchema. This
        will ensure that the order of the atoms and configuration is not change for generation of mapped SMILES and
        isomeric SMILES.

    Returns
    -------
    molecule: OEMol

    """

    molecule = oechem.OEMol()
    for s in symbols:
        molecule.NewAtom(_symbols[s])

    # Add connectivity
    for bond in connectivity:
        a1 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[0]))
        a2 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[1]))
        bond_order = bond[-1]
        if not isinstance(bond_order, int) and not bond_order.is_integer():
            raise ValueError(
                "bond order must be a whole number. A bond order of 1.5 is not allowed"
            )
        molecule.NewBond(a1, a2, int(bond_order))

    # Add geometry
    if molecule.NumAtoms() != geometry.shape[0] / 3:
        raise ValueError(
            "Number of atoms in molecule does not match length of position array"
        )

    molecule.SetCoords(oechem.OEFloatArray(geometry))
    molecule.SetDimension(3)

    if not permute_xyz:
        # Add tag that the geometry is from JSON and shouldn't be changed.
        geom_tag = oechem.OEGetTag("json_geometry")
        molecule.SetData(geom_tag, True)
    oechem.OEDetermineConnectivity(molecule)
    oechem.OEFindRingAtomsAndBonds(molecule)
    # No need to perceive Bond Order because the information was added from the connectivity table. Apparently, this
    # function does many different perceptions under the hood and it can add implicit hydrogens to divalent N that should be negatively charged.
    #oechem.OEPerceiveBondOrders(molecule)
    # This seems to add hydrogens that are not in the json
    #oechem.OEAssignImplicitHydrogens(molecule)
    oechem.OEAssignFormalCharges(molecule)
    oechem.OEAssignAromaticFlags(molecule)
    oechem.OEPerceiveChiral(molecule)
    oechem.OE3DToAtomStereo(molecule)
    oechem.OE3DToBondStereo(molecule)

    return molecule
def canonical_order_molecule_inplace(mol_list):
    """rearrange atoms in molecules to canonical ordering
    based on example from Chaya """
    if not mol_list: return
    toolkit = cmiles.utils._set_toolkit(mol_list[0])
    for oe_mol in mol_list:
        toolkit.canonical_order_atoms(oe_mol)
        # make sure stereochemistry is defined
        if not cmiles.utils.has_stereo_defined(oe_mol):
            oechem.OEPerceiveChiral(oe_mol)
            oechem.OE3DToAtomStereo(oe_mol)
            oechem.OE3DToBondStereo(oe_mol)
Exemple #5
0
def mol_from_json(symbols, connectivity, geometry, permute_xyz=False):
    """
    Generate OEMol from QCSchema molecule specs
    Parameters
    ----------
    inp_molecule: dict
        Must have symbols and connectivity and/or geometry
        Note: If geometry is given, the molecule will have a tag indicating that the goemetry came from QCSchema. This
        will ensure that the order of the atoms and configuration is not change for generation of mapped SMILES and
        isomeric SMILES.

    Returns
    -------
    molecule: OEMol

    """

    molecule = oechem.OEMol()
    for s in symbols:
        molecule.NewAtom(_symbols[s])

    # Add connectivity
    for bond in connectivity:
        a1 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[0]))
        a2 = molecule.GetAtom(oechem.OEHasAtomIdx(bond[1]))
        molecule.NewBond(a1, a2, bond[-1])

    # Add geometry
    if molecule.NumAtoms() != geometry.shape[0] / 3:
        raise ValueError(
            "Number of atoms in molecule does not match length of position array"
        )

    molecule.SetCoords(oechem.OEFloatArray(geometry))
    molecule.SetDimension(3)

    if not permute_xyz:
        # Add tag that the geometry is from JSON and shouldn't be changed.
        geom_tag = oechem.OEGetTag("json_geometry")
        molecule.SetData(geom_tag, True)
    oechem.OEDetermineConnectivity(molecule)
    oechem.OEFindRingAtomsAndBonds(molecule)
    oechem.OEPerceiveBondOrders(molecule)
    # This seems to add hydrogens that are not in the json
    #oechem.OEAssignImplicitHydrogens(molecule)
    oechem.OEAssignFormalCharges(molecule)
    oechem.OEAssignAromaticFlags(molecule)
    oechem.OEPerceiveChiral(molecule)
    oechem.OE3DToAtomStereo(molecule)
    oechem.OE3DToBondStereo(molecule)

    return molecule
Exemple #6
0
def PrepareDepiction(mol, clearcoords=False, suppressH=True):

    oechem.OESetDimensionFromCoords(mol)
    oechem.OEPerceiveChiral(mol)

    if mol.GetDimension() != 2 or clearcoords:
        if mol.GetDimension() == 3:
            oechem.OE3DToBondStereo(mol)
            oechem.OE3DToAtomStereo(mol)
        if suppressH:
            oechem.OESuppressHydrogens(mol)
        oechem.OEAddDepictionHydrogens(mol)

        oechem.OEDepictCoordinates(mol)
        oechem.OEMDLPerceiveBondStereo(mol)

    mol.SetDimension(2)
    return True
Exemple #7
0
def has_stereo_defined(molecule):
    """
    Check if any stereochemistry in undefined.
    Parameters
    ----------
    molecule: OEMol

    Returns
    -------
    bool: True if all stereo chemistry is defined.
        If any stereochemsitry is undefined, raise and exception.

    """

    # perceive stereochemistry
    oechem.OEPerceiveChiral(molecule)
    oechem.OE3DToAtomStereo(molecule)
    oechem.OE3DToBondStereo(molecule)

    unspec_chiral = False
    unspec_db = False
    problematic_atoms = list()
    problematic_bonds = list()
    for atom in molecule.GetAtoms():
        if atom.IsChiral() and not atom.HasStereoSpecified(
                oechem.OEAtomStereo_Tetrahedral):
            # Check if handness is specified
            v = []
            for nbr in atom.GetAtoms():
                v.append(nbr)
            stereo = atom.GetStereo(v, oechem.OEAtomStereo_Tetrahedral)
            if stereo == oechem.OEAtomStereo_Undefined:
                unspec_chiral = True
                problematic_atoms.append(
                    (atom.GetIdx(),
                     oechem.OEGetAtomicSymbol(atom.GetAtomicNum())))
    for bond in molecule.GetBonds():
        if bond.IsChiral() and not bond.HasStereoSpecified(
                oechem.OEBondStereo_CisTrans):
            v = []
            for neigh in bond.GetBgn().GetAtoms():
                if neigh != bond.GetEnd():
                    v.append(neigh)
                    break
            for neigh in bond.GetEnd().GetAtoms():
                if neigh != bond.GetBgn():
                    v.append(neigh)
                    break
            stereo = bond.GetStereo(v, oechem.OEBondStereo_CisTrans)

            if stereo == oechem.OEBondStereo_Undefined:
                unspec_db = True
                a1 = bond.GetBgn()
                a2 = bond.GetEnd()
                a1_idx = a1.GetIdx()
                a2_idx = a2.GetIdx()
                a1_s = oechem.OEGetAtomicSymbol(a1.GetAtomicNum())
                a2_s = oechem.OEGetAtomicSymbol(a2.GetAtomicNum())
                bond_order = bond.GetOrder()
                problematic_bonds.append(
                    (a1_idx, a1_s, a2_idx, a2_s, bond_order))
    if unspec_chiral or unspec_db:
        warnings.warn(
            "Stereochemistry is unspecified. Problematic atoms {}, problematic bonds {}, SMILES: {}"
            .format(problematic_atoms, problematic_bonds,
                    oechem.OEMolToSmiles(molecule)))
        return False
    else:
        return True
def find_smirks_parameters(smiles_list, molecule_paths):
    """Finds the force field parameters which would
    be assigned to a list of molecules defined by the provided
    SMILES patterns.

    Parameters
    ----------
    smiles_list: list of str
        The SMILES patterns of the target molecules
    molecule_paths: list of Path
        The list of molecules that correspond to the SMILES strings (to make it easier to see which molecules
        utilize which parameters)

    Returns
    -------
    dict of str and list of str
        A dictionary with keys of SMIRKS patterns, and
        values of lists of SMILES patterns which would utilize
        those patterns, and the parameter ID in the force field.
    """

    force_field = smirnoff.ForceField('smirnoff99Frosst-1.0.9.offxml')

    smiles_by_smirks = {}
    smiles_by_smirks["Bonds"] = {}
    smiles_by_smirks["Angles"] = {}
    smiles_by_smirks["ProperTorsions"] = {}
    smiles_by_smirks["vdW"] = {}
    smiles_by_smirks["ImproperTorsions"] = {}
    smiles_by_smirks["Electrostatics"] = {}

    # Populate the dictionary using the open force field toolkit.
    for index, smiles in enumerate(smiles_list):

        ifs = oechem.oemolistream()

        if not ifs.open(str(molecule_paths[index])):
            logging.error(
                f'Unable to open {molecule_paths[index]} for reading...')

        ifs.open(str(molecule_paths[index]))
        oe_mols = []
        for mol in ifs.GetOEMols():
            oe_mols.append(oechem.OEMol(mol))
        oechem.OE3DToAtomStereo(oe_mols[0])
        molecule = Molecule.from_openeye(oe_mols[0])

        # molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True)
        topology = Topology.from_molecules([molecule])

        molecule_force_list = force_field.label_molecules(topology)

        for molecule_index, molecule_forces in enumerate(molecule_force_list):
            print(f'Forces for molecule {molecule_index}')
            for force_name, force_dict in molecule_forces.items():
                print(f"\n{force_name}:")
                for (atom_indices, parameter) in force_dict.items():
                    atomstr = ''
                    for idx in atom_indices:
                        atomstr += '%5s' % idx
                    print("atoms: %s  parameter_id: %s  smirks %s" % ([
                        oe_mols[0].GetAtom(oechem.OEHasAtomIdx(i)).GetName()
                        for i in atom_indices
                    ], parameter.id, parameter.smirks))

                    # This is not catching _all_ the atoms that hit a certain parameter.
                    # I think these need to be initialized in the outer loop.
                    # Each parameter is getting a list of length 1.

                    if parameter.id not in smiles_by_smirks[force_name]:
                        smiles_by_smirks[force_name][parameter.id] = {}
                    if "atom_indices" not in smiles_by_smirks[force_name]:
                        smiles_by_smirks[force_name][
                            parameter.id]["atom_indices"] = []
                    if "atom_names" not in smiles_by_smirks[force_name]:
                        smiles_by_smirks[force_name][
                            parameter.id]["atom_names"] = []

                    smiles_by_smirks[force_name][
                        parameter.id]["atom_indices"].append(atom_indices)
                    smiles_by_smirks[force_name][
                        parameter.id]["atom_names"].append([
                            oe_mols[0].GetAtom(
                                oechem.OEHasAtomIdx(i)).GetName()
                            for i in atom_indices
                        ])
                    smiles_by_smirks[force_name][
                        parameter.id]["smirks"] = parameter.smirks

    return smiles_by_smirks
def _extract_oe_fragment(
    molecule: Molecule, atom_indices: Set[int], bond_indices: Set[Tuple[int, int]]
) -> Molecule:

    from openeye import oechem

    oe_molecule = molecule.to_openeye()

    # Restore the map indices as to_openeye does not automatically add them.
    for atom_index, map_index in molecule.properties["atom_map"].items():

        oe_atom = oe_molecule.GetAtom(oechem.OEHasAtomIdx(atom_index))
        oe_atom.SetMapIdx(map_index)

    # Include any Hs bonded to the included atom set so we can retain their map
    # indices.
    for map_index in {*atom_indices}:

        oe_atom = oe_molecule.GetAtom(oechem.OEHasMapIdx(map_index))

        for neighbour in oe_atom.GetAtoms():

            if (
                neighbour.GetAtomicNum() != 1
                or neighbour.GetMapIdx() < 1
                or neighbour.GetMapIdx() in atom_indices
            ):
                continue

            atom_indices.add(neighbour.GetMapIdx())
            bond_indices.add((map_index, neighbour.GetMapIdx()))

    atom_bond_set = oechem.OEAtomBondSet()

    for map_index in atom_indices:
        atom = oe_molecule.GetAtom(oechem.OEHasMapIdx(map_index))
        atom_bond_set.AddAtom(atom)

    for map_index_1, map_index_2 in bond_indices:

        atom_1 = oe_molecule.GetAtom(oechem.OEHasMapIdx(map_index_1))
        atom_2 = oe_molecule.GetAtom(oechem.OEHasMapIdx(map_index_2))

        bond = oe_molecule.GetBond(atom_1, atom_2)

        if not bond:
            raise ValueError(f"{(map_index_1, map_index_2)} is a disconnected bond")

        atom_bond_set.AddBond(bond)

    atom_predicate = oechem.OEIsAtomMember(atom_bond_set.GetAtoms())
    bond_predicate = oechem.OEIsBondMember(atom_bond_set.GetBonds())

    fragment = oechem.OEMol()
    oechem.OESubsetMol(fragment, oe_molecule, atom_predicate, bond_predicate, True)

    oechem.OEAddExplicitHydrogens(fragment)
    oechem.OEPerceiveChiral(fragment)

    # Always restore map?
    # if restore_maps:
    # In some cases (symmetric molecules) this changes the atom map so skip it
    # restore_atom_map(fragment)
    # atom map should be restored for combinatorial fragmentation
    # Perceive stereo and check that defined stereo did not change
    oechem.OEPerceiveChiral(fragment)
    oechem.OE3DToAtomStereo(fragment)
    oechem.OE3DToBondStereo(fragment)

    return Molecule.from_openeye(fragment, allow_undefined_stereo=True)