๐ Generate HELM in Interactive Web Application๏
Problem๏
You want to generate HELM from SMILES in an interactive web application.
See also
[Zhang-2012] publication
Ingredients๏
|
Difficulty Level๏
๐ถ๏ธ ๐ถ๏ธ
Source Code๏
helm_generator
#!/usr/bin/env python3
# (C) 2023 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.
"""Simple streamlit app to HELM generation."""
import base64
import enum
import os
import sys
import streamlit as st
from openeye import oechem, oedepict, oegrapheme
class DepictionStyle(enum.Enum):
"""Utility class for depiction style."""
MonomerHighlight = "Monomer Highlight"
MonomerGraph = "Monomer Graph"
@st.cache_resource
def load_openeye_monomers() -> oechem.OEMonomerSet:
"""Load OpenEye monomer set."""
monomers = oechem.OEMonomerSet()
oechem.OELoadOpenEyeMonomerSet(monomers)
return monomers
def main() -> int:
"""Generate streamlit main application."""
st.set_page_config(
page_title="HELM Generation",
page_icon=":material/image:",
layout="wide",
initial_sidebar_state="collapsed",
)
st.title("HELM Generation with OpenEye Monomer Set!")
monomers = load_openeye_monomers()
# input smiles
mol: None | oechem.OEMolBase = _get_molecule_from_smiles()
# image / helm generation options
image_width, image_height, depiction_style = _generate_depiction_options_section()
depict_options = _generate_depiction_option(depiction_style)
code_set, allow_unspecified_stereo, allow_unmatched_fragment = (
_generate_helm_generation_options_section(monomers)
)
helm_options = oechem.OEHelmGenerationOptions(
code_set, allow_unspecified_stereo, allow_unmatched_fragment
)
result = oechem.OEHelmGenerationResult()
helm: str = oechem.OEMolToHelm(mol, monomers, helm_options, result)
# perceives monomers even when helm generation fails peptide can still be depicted
oechem.OEDetectMonomers(mol, monomers, code_set)
st.divider()
if mol is not None and st.button(
"Generate HELM and image!", icon=":material/image:"
):
if not helm:
st.error(f"HELM generation failed: {result.GetWarning()}")
else:
st.markdown(
f"""### Generated HELM
```text
{helm}
```
"""
)
image = oedepict.OEImage(image_width, image_height)
if depiction_style == DepictionStyle.MonomerHighlight:
oedepict.OEPrepareDepiction(mol)
oegrapheme.OEHighlightMonomers(image, mol, depict_options)
else:
oegrapheme.OEDrawMonomerGraph(image, mol, depict_options)
oedepict.OEDrawCurvedBorder(image, oedepict.OELightGreyPen, 10)
image_str = oedepict.OEWriteImageToString("svg", image)
image_str = image_str.decode("utf-8")
svg_b64 = base64.b64encode(image_str.encode("utf-8")).decode("utf-8")
data_uri = f"data:image/svg+xml;base64,{svg_b64}"
st.components.v1.html(
f'<object data="{data_uri}" type="image/svg+xml" '
f'width="{image_width}" height="{image_height}"></object>',
height=image_height + 20,
)
st.download_button(
label="Download image",
data=image_str,
file_name="helm.svg",
mime="image/svg+xml",
icon=":material/download:",
)
return os.EX_OK
def _generate_depiction_options_section() -> tuple[int, int, DepictionStyle]:
with st.expander("Depiction Options", width=1200):
col_width, col_height, style = st.columns(3, border=True)
with col_width:
image_width = st.slider(
label="Width", min_value=200, max_value=1000, step=25, value=600
)
with col_height:
image_height = st.slider(
label="Height", min_value=200, max_value=1000, step=25, value=400
)
with style:
depict_style = DepictionStyle(
st.selectbox(
"Select depiction style", [t.value for t in DepictionStyle]
)
)
return (image_width, image_height, depict_style)
def _generate_helm_generation_options_section(
monomers: oechem.OEMonomerSet,
) -> tuple[str, bool, bool]:
with st.expander("Helm Generation Options", width=1200):
(
code_set_widget,
options_widget,
) = st.columns(2, border=True)
code_sets = [monomers.GetPrimaryCodeSet()] + [
c for c in monomers.GetCodeSets() if c != monomers.GetPrimaryCodeSet()
]
with code_set_widget:
code_set = st.selectbox("Select code-set", list(code_sets))
with options_widget:
allow_unspecified_stereo = st.toggle("Allow unspecified stereochemistry")
allow_unmatched_fragment = st.toggle("Allow unmatched fragment(s)")
# with unmatched_fragment_widget:
return (code_set, allow_unspecified_stereo, allow_unmatched_fragment)
def _get_molecule_from_smiles() -> None | oechem.OEMolBase:
"""Get a molecule by parsing SMILES."""
mol = oechem.OEGraphMol()
smiles = st.text_input(
"Enter valid SMILES string of a peptide!",
value="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",
)
if not oechem.OESmilesToMol(mol, smiles):
st.error(f"Can not parse: {smiles}")
return mol
def _generate_depiction_option(
style: DepictionStyle,
) -> (
oegrapheme.OEHighlightMonomerDisplayOptions
| oegrapheme.OEMonomerGraphDisplayOptions
):
option: (
oegrapheme.OEHighlightMonomerDisplayOptions
| oegrapheme.OEMonomerGraphDisplayOptions
)
if style == DepictionStyle.MonomerHighlight:
option = oegrapheme.OEHighlightMonomerDisplayOptions()
option.SetTitleLocation(oedepict.OETitleLocation_Hidden)
option.SetAtomStereoStyle(oedepict.OEAtomStereoStyle_Display_All)
option.SetHighlightUnspecifiedStereo(True)
else:
option = oegrapheme.OEMonomerGraphDisplayOptions()
option.SetInteractiveEffect(oedepict.OEInteractiveEffect_Hover)
return option
if __name__ == "__main__":
sys.exit(main())
Usage๏
> helm_generator_app
The Streamlit app will be available at:
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8501
...
The script will automatically load OpenEyeโs built-in monomer sets. It generates HELM from the input SMILES and generates an image of the resulting peptide structure.
The app allows you to: * Input a SMILES string and generate the corresponding HELM. * Visualize the generated HELM as a 2D image in two different styles (monomer-graph and highlight). * Download the generated image in SVG format.
See also
Helm Generation Options section of smiles2helm for more explanation about the available options for HELM generation.
๐ Depict Peptide for peptide image generation.
See also in OEChem TK manual๏
API
OEMonomerSet class
OEReadMonomerSet, OELoadStandardMonomerSet, and OELoadOpenEyeMonomerSet functions
OEDetectMonomers function
OEHelmGenerationOptions class
OEHelmGenerationResult class
OEMolToHelm function
See also in OEGrapheme TK manual๏
API
OEHighlightMonomerDisplayOptions class and OEHighlightMonomers function
OEMonomerGraphDisplayOptions class and OEDrawMonomerGraph function