🆕 Convert HELM File to Molecule File
Problem
You want to convert a HELM file to a molecule file.
See also
[Zhang-2012] publication
Ingredients
|
Difficulty Level
🌶️ 🌶️
Download
Source Code
helms2mols
#!/usr/bin/env python3
# (C) 2026 Cadence Design Systems, Inc. (Cadence)
# All rights reserved.
# TERMS FOR USE OF SAMPLE CODE The software below ("Sample Code") is
# provided to current licensees or subscribers of Cadence products or
# SaaS offerings (each a "Customer").
# Customer is hereby permitted to use, copy, and modify the Sample Code,
# subject to these terms. Cadence claims no rights to Customer's
# modifications. Modification of Sample Code is at Customer's sole and
# exclusive risk. Sample Code may require Customer to have a then
# current license or subscription to the applicable Cadence offering.
# THE SAMPLE CODE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED. OPENEYE DISCLAIMS ALL WARRANTIES, INCLUDING, BUT
# NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
# PARTICULAR PURPOSE AND NONINFRINGEMENT. In no event shall Cadence be
# liable for any damages or liability in connection with the Sample Code
# or its use.
"""Convert a HELM file into a molecule file."""
import argparse
import json
import os
import pathlib
import sys
import rich.console
from openeye import oechem
from rich.progress import BarColumn, Progress, TextColumn
from rich_argparse import HelpPreviewAction, RichHelpFormatter
__SCRIPT_NAME__ = pathlib.Path(__file__).absolute().stem
__SCRIPT_DESC__ = "Convert HELM file into molecule file."
__SCRIPT_TOOLKITS__ = ["oechem"]
__SCRIPT_KEYWORDS__ = ["HELM", "peptide", "peptide-informatics"]
__SCRIPT_CATEGORIES__ = ["peptide-informatics"]
def parse_options() -> argparse.Namespace:
"""Set up command line options."""
parser = argparse.ArgumentParser(
add_help=True,
formatter_class=RichHelpFormatter,
description="[yellow]" + __SCRIPT_DESC__ + "[/yellow]",
)
io_group = parser.add_argument_group("Input/Output options")
io_group.add_argument(
"--helm",
"--helm-file",
metavar="HELM-FILE",
type=str,
required=True,
help="input file of HELM string",
)
io_group.add_argument(
"--mol",
"--mol-file",
metavar="MOL-FILE",
type=str,
required=True,
help="output molecule file",
)
monomers_group = parser.add_argument_group("Monomer set options")
_add_monomer_collection(monomers_group)
verbose_group = parser.add_argument_group("Verbose options")
verbose_group.add_argument(
"--display-failures",
default=False,
action="store_true",
help="display failed HELM conversions",
)
parser.add_argument("--help-image", action=HelpPreviewAction)
parser.add_argument(
"--save-console-svg",
default=False,
action="store_true",
help=f"run command and capture console output in {__SCRIPT_NAME__}.svg file",
)
return parser.parse_args()
def main() -> int:
"""Convert helm file to molecule file."""
args = parse_options()
monomers = _get_monomer_collection(args)
console = rich.console.Console(record=args.save_console_svg)
helms: list[tuple[str, str]] = _read_helms_with_title(args.helm)
ofs = oechem.oemolostream()
if not ofs.open(args.mol):
oechem.OEThrow.Fatal(f"Can not open {args.mol} output file!")
num_failures = 0
mol = oechem.OEGraphMol()
result = oechem.OEHelmParsingResult()
with Progress(
TextColumn("{task.description}"),
BarColumn(bar_width=60),
TextColumn("{task.percentage:3.1f}%"),
transient=True,
disable=len(helms) < 10, # noqa: PLR2004
console=console,
) as progress:
conversion = progress.add_task("[blue]HELM conversion", total=len(helms))
for helm, title in helms:
if oechem.OEHelmToMol(mol, helm, monomers, result):
mol.SetTitle(title)
oechem.OEWriteMolecule(ofs, mol)
else:
num_failures += 1
if args.display_failures:
console.print(helm, markup=False, highlight=False)
console.print(
"[red]"
+ "-" * result.GetErrorPosition()
+ "^ : "
+ result.GetWarning()
+ "[/red]"
)
progress.update(conversion, advance=1)
console.print(
f"[green]Number of successful conversions: {len(helms) - num_failures} [green]"
)
if num_failures != 0:
console.print(f"[red]Number of failed conversions: {num_failures}[/red]")
if args.save_console_svg:
console.save_svg(f"{__SCRIPT_NAME__}.svg", title="output")
return os.EX_OK
def _read_helms_with_title(helm_filename: str) -> list[tuple[str, str]]:
# script will terminate if reading HELM string from file fails
helm_filepath = pathlib.Path(helm_filename)
if not helm_filepath.exists():
oechem.OEThrow.Fatal(f"{helm_filename} file does not exist!")
if helm_filepath.suffix.lower() != ".helm":
oechem.OEThrow.Fatal("Invalid file extension expected .helm!")
helms: list[tuple[str, str]] = []
try:
with helm_filepath.open() as helm_file:
for line in helm_file:
helm, _, title = line.rstrip().partition(" ")
helms.append((helm, title))
except OSError:
oechem.OEThrow.Fatal(f"Can not open {helm_filename} input file!")
if len(helms) == 0:
oechem.OEThrow.Fatal(f"No helm string read from {helm_filename}!")
return []
return helms
class MonomerSetParameter: # noqa: PLW1641
"""Utility class to handle both built-in and user defined monomer sets."""
def __init__(self) -> None: # noqa: D107
self._monomer_sets = ["Standard", "OpenEye", "JSON-FILENAME"]
def __repr__(self) -> str: # noqa: D105
return ",".join(self._monomer_sets)
def __eq__(self, param: object) -> bool: # noqa: D105
if not isinstance(param, str):
return False
if param in ["Standard", "OpenEye"]:
return True
console = rich.console.Console()
monomer_set_filepath = pathlib.Path(param)
if (
not monomer_set_filepath.exists()
or monomer_set_filepath.suffix.lower() != ".json"
):
console.print(f"[red]Invalid monomer set file '{param}' ![/red]")
return False
try:
with monomer_set_filepath.open("r") as json_file:
json.load(json_file)
except json.JSONDecodeError as e:
console.print(f"[red]Invalid monomer set file '{param}' ![/red]")
console.print(f"[red]Error decoding JSON: {e} ![/red]")
return False
return True
def _add_monomer_collection(arg_group: argparse._ArgumentGroup) -> None:
arg_group.add_argument(
"-m",
"--monomers",
type=str,
default="Standard",
choices=[MonomerSetParameter()],
help="built-in monomer-set type or json file of monomers",
)
def _get_monomer_collection(args: argparse.Namespace) -> oechem.OEMonomerSet:
monomers = oechem.OEMonomerSet()
match args.monomers:
case "Standard":
oechem.OELoadStandardMonomerSet(monomers)
case "OpenEye":
oechem.OELoadOpenEyeMonomerSet(monomers)
case _:
oechem.OEReadMonomerSet(monomers, args.monomers)
return monomers
setattr(main, "__SCRIPT_NAME__", __SCRIPT_NAME__)
setattr(main, "__SCRIPT_DESC__", __SCRIPT_DESC__)
setattr(main, "__SCRIPT_TOOLKITS__", __SCRIPT_TOOLKITS__)
setattr(main, "__SCRIPT_KEYWORDS__", __SCRIPT_KEYWORDS__)
setattr(main, "__SCRIPT_CATEGORIES__", __SCRIPT_CATEGORIES__)
if __name__ == "__main__":
sys.exit(main())
Usage
See Download section to download the script.
> helms2mols --help
By default, the helms2mols script uses OEChem TK’s
built-in Standard monomer set for the conversions
(test.helm).
> helms2mols --helm test.helm --mol test.ism
The script will display the number of successful and failed conversions (see also: --display-failures option).
The command above will generate the test.ism file displayed here:
CC[C@H](C)[C@@H](C(=O)N[C@@H](CC(=O)O)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H]([C@@H](C)O)NC(=O)[C@@H]1CCCN1C(=O)[C@H](CCC(=O)O)NC(=O)[C@@H]2CCCN2 Title A
C[C@@H](C(=O)N[C@@H](CC(=O)O)C(=O)N[C@@H](CCC(=O)O)C(=O)N[C@@H](CC(=O)N)C(=O)N[C@@H](CS)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H](CS)N Title
Monomer Set Options
- --monomers OpenEye
OEChem TK’s built-in OpenEye monomer-set can be used with the --monomers OpenEye
parameter.
> helms2mols --helm test-openeye.helm --mol test.ism --monomers OpenEye
Converts (test-openeye.helm)
PEPTIDE1{P.E.P.T.I.D.E}$$$$
PEPTIDE1{[dPro].[dGlu].[dPro].[dThr].[dIle].[dAsp].[dGlu]}$$$$
PEPTIDE1{[Cha].I.R}$$$$
PEPTIDE1{M.E.D.I.C.I.N.E}$$$$
PEPTIDE1{[Cit].Y}$$$$
PEPTIDE1{[Pip].E}$$$$
PEPTIDE1{[App].L.E}$$$$
To (test.ism)
CC[C@H](C)[C@@H](C(=O)N[C@@H](CC(=O)O)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H]([C@@H](C)O)NC(=O)[C@@H]1CCCN1C(=O)[C@H](CCC(=O)O)NC(=O)[C@@H]2CCCN2
CC[C@@H](C)[C@H](C(=O)N[C@H](CC(=O)O)C(=O)N[C@H](CCC(=O)O)C(=O)O)NC(=O)[C@@H]([C@H](C)O)NC(=O)[C@H]1CCCN1C(=O)[C@@H](CCC(=O)O)NC(=O)[C@H]2CCCN2
CC[C@H](C)[C@@H](C(=O)N[C@@H](CCCNC(=N)N)C(=O)O)NC(=O)[C@H](CC1CCCCC1)N
CC[C@H](C)[C@@H](C(=O)N[C@@H](CS)C(=O)N[C@@H]([C@@H](C)CC)C(=O)N[C@@H](CC(=O)N)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H](CC(=O)O)NC(=O)[C@H](CCC(=O)O)NC(=O)[C@H](CCSC)N
c1cc(ccc1C[C@@H](C(=O)O)NC(=O)[C@H](CCCNC(=O)N)N)O
C1CCN[C@@H](C1)C(=O)N[C@@H](CCC(=O)O)C(=O)O
CC(C)C[C@@H](C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)C[C@H]([C@H](Cc1ccccc1)N)O
- --monomers JSON-MONOMER-FILE
The following example shows how to convert a HELM file that uses custom monomers
defined in a json file
(custom-monomers.json)
> helms2mols --helm test-custom.helm --mol test.ism --monomers custom-monomers.json
Converts (test-custom.helm)
PEPTIDE1{[Cys].[Pro].[Phe(4-F)].[Ala].[Cys]}$PEPTIDE1,PEPTIDE1,1:R3-5:R3$$$
PEPTIDE1{[Cys].[Pro].[D-Phe].[Ala].[Cys]}$PEPTIDE1,PEPTIDE1,1:R3-5:R3$$$
PEPTIDE1{[Cys].[Pro].[Phe].[Ala].[Cys]}$PEPTIDE1,PEPTIDE1,1:R3-5:R3$$$
PEPTIDE1{[Cys].[Pro].[mePhe].[Ala].[Cys]}$PEPTIDE1,PEPTIDE1,1:R3-5:R3$$$
PEPTIDE1{[Cys].[Pro].[D-mePhe].[Ala].[Cys]}$PEPTIDE1,PEPTIDE1,1:R3-5:R3$$$
To (test.ism)
CC[C@H](C)[C@@H](C(=O)N[C@@H](CC(=O)O)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H]([C@@H](C)O)NC(=O)[C@@H]1CCCN1C(=O)[C@H](CCC(=O)O)NC(=O)[C@@H]2CCCN2 Title A
C[C@@H](C(=O)N[C@@H](CC(=O)O)C(=O)N[C@@H](CCC(=O)O)C(=O)N[C@@H](CC(=O)N)C(=O)N[C@@H](CS)C(=O)N[C@@H](CCC(=O)O)C(=O)O)NC(=O)[C@H](CS)N Title
Verbose Options
- --display-failures
By turning on the --display-failures option, the script
will also show all failed conversions with the corresponding warning message
(test.helm).
> helms2mols --helm test.helm --mol test.ism --display-failures
See also in OEChem TK manual
API
OEMonomerSet class
OEReadMonomerSet, OELoadStandardMonomerSet, and OELoadOpenEyeMonomerSet functions
OEHelmToMol function
OEHelmParsingResult class