Source code for qm_atlas.wrappers.unicon

# Usage: unicon -i example.sdf
#  Available options:
#
#  General options:
#   -h [ --help ]                    Prints help message
#   -v [ --verbosity ] arg           Set verbosity level
#                                    (0 = Quiet, 1 = Errors, 2 = Warnings, 3 =
#                                    Info)
#   --jobs arg (=1)                  Number of threads to use (0 = auto, 1 =
#                                    single [default]), e.g.: -j 4
#
#  Input options:
#   -i [ --input ] arg               Input file(s) suffix is required or
#                                    directories. Several input files or
#                                    directories can be given separated by
#                                    spaces, (e.g. -i a.mol2 b.sdf dataDir)
#   --inFormat arg                   Input file format (e.g. MOL(*.mol)
#                                    MOL2(*.mol2) PDB(*.pdb) SDF(*.sdf)
#                                    SMILES(*.smi *.smiles) INCHI(*.inchi)
#   --from arg                       The start entry of the input to be used for
#                                    processing
#   --to arg                         The last entry of the input to be used for
#                                    processing
#   --multi                          Convert all components into multiple entries
#                                    (Largest Component is converted by default).
#
#  Output options:
#   -o [ --output ] arg              Output file, suffix is required.
#   --outFormat arg                  Output file format (e.g. MOL2(*.mol2)
#                                    SDF(*.sdf) SMILES(*.smi *.smiles))
#   -s [ --split ] arg               Number of input entries the split files will
#                                    contain
#   -k [ --key ]                     In case of InChI output use 'key' option to
#                                    write InChI keys
#
#  Configuration:
#   -t [ --tautomer ] arg            Generate tautomers:
#                                    topscoring = enumerate only best tautomers
#                                    ensemble = enumerate all tautomers
#                                    single = generate only one normalized tautomer
#   -p [ --protonation ] arg         Generate protonation states:
#                                    topscoring = enumerate only best protonation states
#                                    ensemble = enumerate all protonation states
#                                    single = generate only one normalized protonation state
#   -m [ --maxNofConfs ] arg (=250)  Set the maximum number of generated conformations
#   -c [ --conformer ] arg           Generate conformers with quality level
#                                    (1 = Fast, 2 = Best)
#   -g [ --generate ] arg (=1)       Generate coordinates for output
#                                    (1 = keep original, 2 = 2D-Coordinates, 3 = 3D-Coordinates)
#   --hydrogens                      Consider hydrogen clashes during conformation and coordinate
#                                    generation
#   --extract arg                    Extract ligand with given name
#
#  License:
#   --license arg                    To reactivate the executable, please provide
#                                    a new license key.
#

import logging
from pathlib import Path

from ppqm import chembridge
from ppqm.utils import WorkDir
from rdkit.Chem import Mol

from qm_atlas import constants, utils
from qm_atlas.software_environment import SOFTWARE_CONFIG
from qm_atlas.wrappers.common import run_command

_logger = logging.getLogger(__name__)


def _get_tautomers_from_smiles(
    smiles,
    scr=constants.DEFAULT_SCR,
    return_canonical=True,
    cmd=None,
    keep_files=False,
):
    """Internal function to generate tautomers from SMILES string."""

    # Get command from environment manager
    software_env_manager = SOFTWARE_CONFIG.get_environment_manager()
    if cmd is None:
        cmd = software_env_manager.get_command("unicon", "unicon")

    temp = WorkDir(dir=scr, prefix="unicon_", keep=keep_files)
    work_dir = temp.get_path()

    ext = "smi"
    filename = "_tmp_unicon_.smi"

    filename_in = work_dir / filename
    filename_out = work_dir / (filename + ".o")

    with utils.open_utf8(filename_in, "w") as f:
        f.write(smiles)

    # empty file
    with utils.open_utf8(filename_out, "w") as f:
        pass

    options = [
        cmd,
        f"-i {filename}",
        f"-o {filename}.o",
        "-t=ensemble",
        "--inFormat smiles",
        f"--outFormat {ext}",
    ]

    cmd = " ".join(options)

    cmd = f"cd {work_dir}; " + cmd

    _logger.debug(cmd)

    env = software_env_manager.get_run_environment("unicon")
    stdout, stderr = run_command(cmd, env=env)
    stdout = "" if stdout is None else stdout
    stderr = "" if stderr is None else stderr
    lines = stdout + "\n" + stderr
    lines = lines.split("\n")

    with utils.open_utf8(filename_out) as f:
        tautomers = f.readlines()
        tautomers = [x.strip() for x in tautomers]

    if len(tautomers) == 0:

        for line in lines:
            line = line.lower()
            if "error" in line:
                line = line.replace("error", "").replace(":", "")
                _logger.error(line)

        raise RuntimeError("UNICON tautomer generation failed")

    # Clean duplicates
    tautomers = list(set(tautomers))

    if return_canonical:
        tautomers = [chembridge.get_canonical_smiles(smi) for smi in tautomers]

    return tautomers


[docs] def get_tautomers( mol: Mol, scr: Path = constants.DEFAULT_SCR, keep_files: bool = False, return_canonical: bool = True, cmd: str | None = None, ) -> list[Mol]: """Generate tautomers for a molecule using UNICON. See references [1], [2] for details on the UNICON tautomer generation algorithm. Args: mol (Mol): The rdkit Mol to generate tautomers for. scr (Path): The scratch directory to use for temporary files. Defaults to constants.DEFAULT_SCR. keep_files (bool): Whether to keep the temporary files. Defaults to False. Useful for debugging. return_canonical (bool): Whether to return canonical SMILES for the generated tautomers. Defaults to True. cmd (str | None): The command to use for unicon. If None, the command is resolved from the software configuration. Raises: RuntimeError: If the UNICON tautomer generation fails. Returns: list[Mol]: A list of rdkit Mol objects representing the generated tautomers. References: [1] Sommer, K., Friedrich, N.-O., Bietz, S., Hilbig, M., Inhester, T. & Rarey, M. UNICON: A Powerful and Easy-to-Use Compound Library Converter. *J. Chem. Inf. Model.* **56**, 1105-1111 (2016). https://doi.org/10.1021/acs.jcim.6b00069 [2] Urbaczek, S., Kolodzik, A. & Rarey, M. The Valence State Combination Model: A Generic Framework for Handling Tautomers and Protonation States. *J. Chem. Inf. Model.* **54**, 756-766 (2014). https://doi.org/10.1021/ci400724v """ smiles = chembridge.molobj_to_smiles(mol) smiles_list = _get_tautomers_from_smiles( smiles, scr=scr, keep_files=keep_files, return_canonical=return_canonical, cmd=cmd, ) rtn_list = [] for tausmi in smiles_list: taut_molobj = chembridge.smiles_to_molobj(tausmi) rtn_list.append(taut_molobj) return rtn_list