How To Create A Chemical 3d Model In Python

8 min read

How to Create a Chemical 3D Model in Python

Introduction

Creating a three‑dimensional (3‑D) representation of a molecule is a fundamental skill for chemists, material scientists, and anyone who works with computational chemistry. A 3‑D model lets you visualise bond angles, steric clashes, and intermolecular interactions that are invisible in a flat 2‑D drawing. In Python, the process can be scripted, automated, and integrated into larger workflows such as virtual screening, molecular dynamics, or machine‑learning pipelines. This article walks you through the entire pipeline—from obtaining a chemical identifier to rendering an interactive 3‑D view—using widely‑available, open‑source Python libraries. By the end, you will be able to generate, optimise, and visualise reliable 3‑D structures of small organic molecules, peptides, or even simple inorganic complexes.


Detailed Explanation

Why Python for 3‑D Molecular Modelling?

Python’s strength lies in its rich ecosystem of scientific libraries that handle chemistry‑specific tasks without requiring low‑level programming. The most common stack for building 3‑D models includes:

Library Primary Role Typical Use
RDKit Cheminformatics, SMILES parsing, 2‑D → 3‑D conversion, force‑field minimisation Generating initial coordinates, adding hydrogens, assigning bond orders
Open Babel (via openbabel or pybel) File format conversion, additional force fields (MMFF94, UFF) Alternative geometry optimisation, batch file handling
NGLView Interactive 3‑D visualisation in Jupyter notebooks Rendering balls‑and‑sticks, surfaces, electrostatic maps
Py3Dmol Lightweight WebGL‑based viewer (works outside notebooks) Quick inline visualisation in scripts or HTML reports
ASE (Atomic Simulation Environment) Geometry optimisation with DFT‑calculators or classical force fields Running more refined minimisations or short MD trajectories
MDAnalysis Trajectory analysis (if you later run dynamics) Extracting distances, angles, RMSD, etc.

Honestly, this part trips people up more than it should.

The workflow typically follows these logical steps:

  1. Input acquisition – SMILES, InChI, PDB ID, or a local file.
  2. Molecule object creation – RDKit’s Mol from the input.
  3. Hydrogen addition & sanitisation – Ensuring correct valence and aromaticity.
  4. 3‑D coordinate generation – Embedding with distance geometry or ETKDG.
  5. Energy minimisation – Applying a force field (UFF, MMFF94) to relieve strain.
  6. Visualization – Featuring the final coordinates in an interactive viewer.

Each step can be performed with a few lines of code, making the whole process highly reproducible Simple, but easy to overlook..


Step‑by‑Step or Concept Breakdown

Below is a concrete, copy‑paste‑ready example that builds a 3‑D model of aspirin (acetylsalicylic acid) and displays it in a Jupyter notebook. Feel free to replace the SMILES with any other molecule.

1. Install the required packages

# In a terminal or notebook cell
pip install rdkit-pypi nglview py3dmol
# Optional: Open Babel for alternative minimisation
conda install -c conda-forge openbabel

Note: RDKit is distributed as rdkit-pypi on PyPI; the conda‑forge channel also provides a compiled version Surprisingly effective..

2. Import libraries and define the molecule

from rdkit import Chem
from rdkit.Chem import AllChem
import nglview as nv
import py3Dmol

# SMILES for aspirin
smiles = "CC(=O)OC1=CC=CC=C1C(=O)O"
mol = Chem.MolFromSmiles(smiles)
if mol is None:
    raise ValueError("Invalid SMILES string")

3. Add hydrogens and sanitise

mol = Chem.AddHs(mol)          # explicit H atoms
AllChem.SanitizeMol(mol)       # checks valence, aromaticity, etc.

4. Generate initial 3‑D coordinates

# ETKDG v3 is a reliable distance‑geometry method
params = AllChem.ETKDGv3()
params.randomSeed = 42        # for reproducibility
AllChem.EmbedMolecule(mol, params)

5. Minimise the geometry with a force field

# Using the Universal Force Field (UFF)
AllChem.UFFOptimizeMolecule(mol, maxIters=200)
# Alternatively, MMFF94 (requires RDKit's MMFF build)
# AllChem.MMFFOptimizeMolecule(mol)

6. Extract coordinates for visualisation

conf = mol.GetConformer()
coords = conf.GetPositions()   # numpy‑like array of shape (N_atoms, 3)
atoms = [atom.GetSymbol() for atom in mol.GetAtoms()]

7. Visualise with NGLView (Jupyter)

# Convert RDKit mol to PDB string for NGLView
pdb_block = Chem.MolToPDBBlock(mol)
view = nv.show_rdkit(mol)      # quick RDKit‑specific viewer
# Or use a generic NGLView structure:
view = nv.show_structure(file=pdb_block, format='pdb')
view.add_ball_and_stick()
view.add_spacefill(radius=0.5)
view

8. (Optional) Export a static image with py3Dmol

viewer = py3Dmol.view(width=600, height=400)
viewer.addModel(Chem.MolToPDBBlock(mol), 'pdb')
viewer.setStyle({'stick': {}, 'sphere': {'scale': 0.3}})
viewer.zoomTo()
viewer.show()

What each block does

Block Purpose
Import & SMILES parsing Turns a text representation into an RDKit molecule object.
EmbedMolecule (ETKDG) Generates a rough 3‑D geometry that respects bond lengths and angles.
GetConformer / GetPositions Pulls out the Cartesian coordinates needed for visualisation or further calculations.
AddHs / SanitizeMol Guarantees correct hydrogen count and flags any valence errors. Even so,
UFFOptimizeMolecule Refines the geometry by minimizing steric strain using a classical force field.
NGLView / py3Dmol Provides an interactive, web‑GL based viewer that can be embedded in notebooks or exported as HTML.

You can repeat the

9. Compute common molecular descriptors

from rdkit.Chem import Descriptors, Lipinski

mol_weight = Descriptors.ExactMolWt(mol)      # exact molecular weight
logp       = Descriptors.NumHDonors(mol)      # hydrogen‑bond donors
hba_count  = Descriptors.MolLogP(mol)         # estimated log P
hbd_count  = Descriptors.NumHAcceptors(mol)   # hydrogen‑bond acceptors
rotatable  = Descriptors.

print(f"MW={mol_weight:.3f} Da, LogP={logp:.2f}, HBD={hbd_count}, HBA={hba_count}, RotB={rotatable}")

These descriptors give a quick overview of the physicochemical profile of aspirin and are often required as input for downstream QSAR or docking studies Simple, but easy to overlook..


10. Generate a Morgan fingerprint for similarity searching

# radius = 2, 2048‑bit fingerprint – a standard choice for medicinal‑chemistry datasets
fp = AllChem.GetMorganFingerprintAsBitVect(mol, radius=2, nBits=2048)

# Convert to a list of integers for easy handling (e.g., with pandas or scikit‑learn)
fp_bits = [int(bit) for bit in fp]
print(f"Fingerprint generated: {len(fp_bits)} bits")

The fingerprint can be stored in a database or used with fingerprint‑based similarity metrics (Tanimoto, Dice, etc.) to locate chemically related compounds.


11. Export the 3‑D structure to an SDF file

from rdkit.Chem import SDWriter

with SDWriter('aspirin.sdf') as writer:
    writer.write(mol)

print("SDF written to aspirin.sdf")

The SDF format preserves both the 3‑D coordinates and a rich set of property fields, making it compatible with most external cheminformatics tools (e.g., AutoDock, Schrödinger, Open Babel).


12. Highlight specific functional groups in an interactive viewer

import py3Dmol

# Locate atoms belonging to the carboxylic acid (‑COOH) and ester (‑COO‑) groups
ca_indices = [i for i, a in enumerate(mol.GetAtoms()) if a.GetSymbol() == 'C' and any(nb.GetSymbol() == 'O' for nb in a.GetNeighbors()) and a.GetIdx() in 
              [n.GetIdx() for n in a.GetNeighbors() if n.GetSymbol() == 'O' and n.GetIdx() != i][0]]  # C of COOH
# A simpler approach: use the atom names after sanitization
ca_indices = [i for i, a in enumerate(mol.GetAtoms()) if a.GetName() == 'C' and any(nb.GetSymbol() == 'O' for nb in a.GetNeighbors())]

ester_indices = [i for i, a in enumerate(mol.GetAtoms()) if a.GetNeighbors()) and 
                 any(nb2.Here's the thing — getSymbol() == 'O' and any(nb. GetSymbol() == 'C' for nb in a.GetSymbol() == 'O' for nb2 in nb.

viewer = py3Dmol.view(width=800, height=600)
viewer.addModel(Chem.MolToPDBBlock(mol), 'pdb')

# Color the carboxylic carbon red and the ester oxygens blue
viewer.addStyle({'atomIdx': ca_indices, 'color': 'red', 'stick': {} })
viewer.addStyle({'atomIdx': ester_indices, 'color': 'blue', 'sphere': {'radius': 0.3}})

viewer.zoomTo()
viewer.show()

This snippet demonstrates how to draw attention to the key pharmacophores of aspirin directly inside a web‑GL viewer, which is handy for teaching or for quick visual inspection.


13. Write Cartesian coordinates to an XYZ file

with open('aspirin.xyz', 'w') as f:
    f.write(f"{mol.GetNumAtoms()}\n")
    for atom in mol.GetAtoms():
        pos = conf.GetPosition(atom.GetIdx())
        f.write(f"{atom.GetSymbol():<4} {pos[0]:.4f} {pos[1]:.4f} {pos[2]:.4f}\n")
print("XYZ file generated: aspirin.xyz")

The XYZ file is a plain‑text format widely accepted by molecular‑dynamics packages and visualizers that do not rely on RDKit Easy to understand, harder to ignore..


14. Produce a 2‑D SVG rendering for static reports

AllChem.Compute2DCoords(mol)                     # ensures chemically sensible planar layout
svg = AllChem.Draw.MolToImage(mol, size=(400, 300), kekulize=True)
svg.save('aspirin_2d.svg')
print("2‑D SVG saved as aspirin_2d.svg")

A clean 2‑D depiction is often required for publications, patents, or slide decks, and RDKit’s perception‑based layout yields chemically accurate bond orders.


15. Summary and conclusion

The workflow presented moves from a simple SMILES string to a fully optimized three‑dimensional model of aspirin, then enriches that model with quantitative descriptors, fingerprints, and multiple export formats. By leveraging RDKit for cheminformatics tasks and NGLView/py3Dmol for interactive visualisation, the pipeline becomes both reproducible (thanks to a fixed random seed) and extensible (easy to plug into larger screening or docking campaigns) Easy to understand, harder to ignore..

In practice, the generated descriptors and fingerprints can feed into QSAR models, virtual libraries, or machine‑learning classifiers, while the 3‑D structures serve as input for docking, free‑energy calculations, or molecular‑dynamics simulations. The ability to export to SDF, XYZ, or SVG ensures seamless integration with external software and reporting tools Less friction, more output..

Conclusion – This end‑to‑end example demonstrates a practical, reproducible cheminformatics pipeline: parsing → sanitising → 3‑D generation → minimisation → property analysis → visualisation → data export. By following these steps, researchers can quickly inspect, analyse, and share molecular structures, laying a solid foundation for downstream computational projects.

New Content

New on the Blog

Explore More

See More Like This

Thank you for reading about How To Create A Chemical 3d Model In Python. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home