"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"smiles = '[NH:4]1[CH2:3][CH2:2][NH:1][CH2:6][CH2:5]1'\n",
"mol = Chem.MolFromSmiles(smiles)\n",
"mol"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's use default parameter for embedding, which uses ETKDG version 1, and generate 500 conformers. "
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [],
"source": [
"# ETKDG v1 \n",
"params = Chem.rdDistGeom.EmbedParameters()\n",
"params.randomSeed = 0xf00d\n",
"params.clearConfs = True\n",
"\n",
"mol_h = Chem.AddHs(mol)\n",
"cids = AllChem.EmbedMultipleConfs(mol_h, numConfs=500, params=params)"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [],
"source": [
"# atom mapping\n",
"atoms = {a.GetAtomMapNum(): a.GetIdx() for a in mol.GetAtoms() if a.GetAtomMapNum() > 0}\n",
"\n",
"# torsion angle atom indices\n",
"torsions = [\n",
" (atoms[1], atoms[2], atoms[3], atoms[4]),\n",
" (atoms[2], atoms[3], atoms[4], atoms[5]),\n",
" (atoms[3], atoms[4], atoms[5], atoms[6]),\n",
" (atoms[4], atoms[5], atoms[6], atoms[1]),\n",
" (atoms[5], atoms[6], atoms[1], atoms[2]),\n",
" (atoms[6], atoms[1], atoms[2], atoms[3]),\n",
"]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"I'm going to define a simple check in chain vs boat vs twisted conformers of cyclohexane ring. If all six torsions are near +/- 60˚ (margin 30˚), it is considered a chair conformation. If two opposite torsions are close to 0˚ (margin 30˚) and the other remaining torsion angles are around +/- 60˚, then it is considered a boat conformation. The rest are considered as a twisted conformation."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
"def ring_conformers(mol):\n",
" angles = []\n",
" confs = []\n",
"\n",
" for conf in mol.GetConformers():\n",
" angs = np.abs([Chem.rdMolTransforms.GetDihedralDeg(conf, *t) for t in torsions])\n",
" angles.append(angs)\n",
"\n",
" if np.all(np.abs((angs - 60)/60) < 0.66): \n",
" confs.append('chair')\n",
" elif angs[0] < 30 and angs[3] < 30 and np.all(np.abs((angs[[1,3,4,5]] - 60)/60) < 0.5):\n",
" confs.append('boat')\n",
" elif angs[1] < 30 and angs[4] < 30 and np.all(np.abs((angs[[0,2,3,4]] - 60)/60) < 0.5):\n",
" confs.append('boat') \n",
" elif angs[2] < 30 and angs[5] < 30 and np.all(np.abs((angs[[0,1,3,4]] - 60)/60) < 0.5):\n",
" confs.append('boat') \n",
" else:\n",
" confs.append('twisted')\n",
" return confs"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'chair': 254, 'twisted': 228, 'boat': 18})"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from collections import Counter\n",
"confs = ring_conformers(mol_h)\n",
"Counter(confs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So, I got 203 chair (40%), 18 boat (4%), and 279 twisted (56%) conformers. Let's take a look at some conformers and make sure we got the right conformers. First chair conformation."
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'chair']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And a boat conformation:"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'boat']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And finally a twisted conformation:"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'twisted']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Optimize Geometry using MMFF\n",
"\n",
"Optimizing the Geometry using MMFF does the number of chair conformations and reduces twisted conformers."
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [],
"source": [
"for conf in mol_h.GetConformers():\n",
" AllChem.MMFFOptimizeMolecule(mol_h, confId=conf.GetId())"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'chair': 366, 'twisted': 131, 'boat': 3})"
]
},
"execution_count": 66,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"confs = ring_conformers(mol_h)\n",
"Counter(confs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Still about 50% of the conformers remain in the twisted conformation. "
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'twisted']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ETKDG version 3\n",
"\n",
"I commented this issue on Greg's blog and was suggested to try ETKDG version 3 algorithm [[ref]](https://pubs.acs.org/doi/abs/10.1021/acs.jcim.0c00025). Here's the result. "
]
},
{
"cell_type": "code",
"execution_count": 84,
"metadata": {},
"outputs": [],
"source": [
"# ETKDG v3\n",
"params = Chem.rdDistGeom.srETKDGv3()\n",
"params.randomSeed = 0xf00d\n",
"params.clearConfs = True\n",
"\n",
"cids = AllChem.EmbedMultipleConfs(mol_h, numConfs=500, params=params)"
]
},
{
"cell_type": "code",
"execution_count": 85,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'chair': 500})"
]
},
"execution_count": 85,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"confs = ring_conformers(mol_h)\n",
"Counter(confs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All 500 chair conformation even without doing minimization! But strange things were happening when I actually looked at some conformers. It appears the torsion angle requirements are all satisfied, but the ring was actually very much twisted. "
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"#conf_idx = [i for i, c in enumerate(confs) if c == 'chair']\n",
"#shuffle(conf_idx)\n",
"conf_idx = [443]\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "code",
"execution_count": 105,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[-46.976379180669305, -46.49507991432351, 70.73380111873767, -47.28801834351138, -46.701253412582766, 70.21989187631925]\n"
]
}
],
"source": [
"conf_idx = 443\n",
"conf = mol_h.GetConformer(conf_idx)\n",
"angs = [Chem.rdMolTransforms.GetDihedralDeg(conf, *t) for t in torsions]\n",
"print(angs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the six-membered ring, the signs of the torsion angle should alternate around positive and negative 60 degree. In my naive torsion angle check, I only checked if the absolute value of the torsion angles were around 60˚. However, in this case the torsion angles are arranged in around (-60, -60, +60, -60, -60, +60), which makes it twisted. Let's count how many of these are in our conformers. "
]
},
{
"cell_type": "code",
"execution_count": 125,
"metadata": {},
"outputs": [],
"source": [
"confs = []\n",
"sums = []\n",
"\n",
"for conf in mol_h.GetConformers():\n",
" angs = np.array([Chem.rdMolTransforms.GetDihedralDeg(conf, *t) for t in torsions])\n",
" \n",
" # check if pos/neg values are alternating\n",
" signs = angs / np.abs(angs)\n",
" if np.sum(signs[1:] + signs[:-1]) == 0:\n",
" confs.append('chair')\n",
" else:\n",
" confs.append('twisted')"
]
},
{
"cell_type": "code",
"execution_count": 126,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Counter({'chair': 358, 'twisted': 142})"
]
},
"execution_count": 126,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"Counter(confs)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"OK, so turns out, 358 (72%) of the conformers were actually in chair conformation and 142 (28%) of the conformers were in twisted conformers. Let's take a look at some of the chair and twisted conformers."
]
},
{
"cell_type": "code",
"execution_count": 127,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'chair']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A twisted conformer:"
]
},
{
"cell_type": "code",
"execution_count": 130,
"metadata": {},
"outputs": [
{
"data": {
"application/3dmoljs_load.v0": "\n
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol
\n
\n",
"text/html": [
"\n",
"
You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n",
" jupyter labextension install jupyterlab_3dmol
\n",
"
\n",
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#collapse-hide\n",
"from random import shuffle\n",
"conf_idx = [i for i, c in enumerate(confs) if c == 'twisted']\n",
"shuffle(conf_idx)\n",
"\n",
"viewer = py3Dmol.view(width=300, height=300)\n",
"viewer.addModel(Chem.MolToMolBlock(mol_h, confId=conf_idx[0]), 'mol')\n",
"viewer.setStyle({\"stick\":{}})\n",
"viewer.zoomTo()\n",
"viewer.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conclusion\n",
"\n",
"I have used piperazine as an example because the molecule I was working had one, but I imagine cyclohexane may have the similar issue. The new ETKDG v3 algorithm does a good job getting chair conformation without FF optimization, but it could still produce seriously twisted conformers sometimes. User should check their conformer in such case with care before proceeding. \n",
"\n",
"I have not looked into how RDKit's ETKDG algorithm is implemented yet. If it is simple, I might be able to contribute. Otherwise, I can use the conformation checker I used here to filter out non-chair conformers."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}