BioGraph-AI / src /conformers.py
VSRAJHAVEL's picture
Upload folder using huggingface_hub
2fd4f2f verified
Raw History Blame Contribute Delete
4.21 kB
"""
3D Conformer Generation and Interactive Molecular Modeling.
Generates energetic 3D molecular conformations using ETKDG and MMFF94 force field,
and constructs py3Dmol visualizer HTML blocks for Streamlit components.
"""
from typing import Tuple, Optional
from rdkit import Chem
from rdkit.Chem import AllChem
import py3Dmol
def generate_3d_conformer(smiles_or_mol: any, seed: int = 42) -> Tuple[Optional[Chem.Mol], Optional[str], Optional[float], str]:
"""
Generate an energy-minimized 3D conformer for a molecule.
Args:
smiles_or_mol: SMILES string or RDKit Mol object.
seed: Random seed for deterministic coordinate embedding.
Returns:
Tuple of (mol_3d, mol_block_str, energy_kcal_per_mol, status_message)
"""
if isinstance(smiles_or_mol, str):
mol = Chem.MolFromSmiles(smiles_or_mol)
else:
mol = Chem.Mol(smiles_or_mol)
if mol is None:
return None, None, None, "Invalid chemical structure"
mol_h = Chem.AddHs(mol)
# Use ETKDG embedding
params = AllChem.ETKDGv3() if hasattr(AllChem, 'ETKDGv3') else AllChem.ETKDG()
params.randomSeed = seed
embed_code = AllChem.EmbedMolecule(mol_h, params)
if embed_code != 0:
# Fallback with relaxed distance geometry
embed_code = AllChem.EmbedMolecule(mol_h, randomSeed=seed, useRandomCoords=True)
if embed_code != 0:
return None, None, None, "Failed to embed 3D coordinates"
# MMFF94 Force Field Optimization
energy = None
status = "MMFF94 optimized"
if AllChem.MMFFHasAllMoleculeParams(mol_h):
mmff_props = AllChem.MMFFGetMoleculeProperties(mol_h)
ff = AllChem.MMFFGetMoleculeForceField(mol_h, mmff_props)
if ff:
ff.Initialize()
ff.Minimize(maxIts=500)
energy = float(ff.CalcEnergy())
else:
# Fallback to Universal Force Field (UFF)
if AllChem.UFFHasAllMoleculeParams(mol_h):
ff = AllChem.UFFGetMoleculeForceField(mol_h)
if ff:
ff.Initialize()
ff.Minimize(maxIts=500)
energy = float(ff.CalcEnergy())
status = "UFF optimized (MMFF params unavailable for this element)"
mol_block = Chem.MolToMolBlock(mol_h)
return mol_h, mol_block, energy, status
def render_3dmol_html(
mol_block: str,
style: str = "stick",
surface_type: str = "none",
spin: bool = False,
width: int = 500,
height: int = 400
) -> str:
"""
Generate self-contained HTML for interactive 3D molecular visualization using py3Dmol.
Args:
mol_block: MDL MolBlock representation of the 3D conformer.
style: Visual representation ('stick', 'ball_and_stick', 'sphere', 'wireframe').
surface_type: Molecular surface representation ('none', 'vdw', 'sas', 'ses').
spin: Whether to enable auto-rotation.
width: Viewer width in pixels.
height: Viewer height in pixels.
Returns:
HTML string suitable for st.components.v1.html.
"""
view = py3Dmol.view(width=width, height=height)
view.addModel(mol_block, "mol")
if style == "stick":
view.setStyle({'stick': {'radius': 0.18, 'colorscheme': 'cyanCarbon'}})
elif style == "ball_and_stick":
view.setStyle({
'stick': {'radius': 0.14, 'colorscheme': 'cyanCarbon'},
'sphere': {'scale': 0.28, 'colorscheme': 'cyanCarbon'}
})
elif style == "sphere":
view.setStyle({'sphere': {'scale': 0.85, 'colorscheme': 'cyanCarbon'}})
elif style == "wireframe":
view.setStyle({'line': {'linewidth': 2.0, 'colorscheme': 'cyanCarbon'}})
else:
view.setStyle({'stick': {}})
# Optional molecular surface
surf_map = {
'vdw': py3Dmol.VDW,
'sas': py3Dmol.SAS,
'ses': py3Dmol.SES
}
if surface_type.lower() in surf_map:
view.addSurface(surf_map[surface_type.lower()], {'opacity': 0.65, 'color': 'lightblue'})
if spin:
view.spin(True)
view.zoomTo()
view.setBackgroundColor('#0f172a') # Dark slate background for premium biotech feel
return view._make_html()