Chapter 2: Stereochemistry - Geometics Isomers#

1. Introduction#

Previously, we discussed optical isomers (enantiomers)—molecules containing chiral centers that are non-superimposable mirror images of each other.

Geometric isomerism, also called cis-trans or E-Z isomerism, arises when restricted rotation creates distinct spatial arrangements of substituents around double bonds or within rings. Geometric isomers often exhibit dramatically different biological activities—one isomer may be a potent therapeutic while the other is inactive or even toxic.


2. Key Concepts and Definitions#

  • Geometric Isomers (Stereoisomers): Compounds with identical connectivity but different spatial arrangements due to restricted rotation, resulting in non-interconvertible configurations at room temperature.

  • Cis/Trans Nomenclature: Simple system where “cis” indicates substituents on the same side and “trans” indicates substituents on opposite sides; limited to cases with two identical groups.

  • Restricted Rotation: Inability of substituents to freely rotate around a bond, caused by π-bond character (C=C, C=N) or ring constraints, maintaining distinct geometric arrangements.

  • Double Bond Rigidity: The π-bond in C=C or C=N bonds prevents rotation (~65 kcal/mol barrier), locking substituents in fixed positions and creating geometric isomers.

  • Ring Constraints: Cyclic structures restrict rotation around C-C single bonds, creating cis/trans arrangements (e.g., substituents above/below the ring plane).


3. Main Content#

3.1 Molecular Origins of Geometric Isomerism#

Geometric isomerism requires two conditions:

  • (1) restricted rotation, and

  • (2) two different substituents on each end of the restricted bond. The most common sources are:

C=C Double Bonds: The π-bond prevents rotation, creating a planar rigid structure. Each carbon must have two different substituents to generate isomers. For example, 2-butene exists as cis and trans isomers

C=N Double Bonds: Found in oximes, hydrazones, and imines used in drug synthesis and as active pharmaceutical ingredients. The same rotation restriction applies.

Ring Systems: Cyclic structures (cyclopropane through larger rings) constrain rotation. Substituents can be cis (same face of ring) or trans (opposite faces), significantly affecting molecular shape and rigidity.

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem
import numpy as np
from IPython.display import HTML

def create_geometric_isomer_comparison():
    """
    Create and visualize cis and trans isomers of 2-butene
    Returns HTML with interactive 3D viewers
    """
    # 1. SETUP MOLECULES
    # SMILES for cis and trans 2-butene
    cis_smiles = "C/C=C\\C"  # Z configuration
    trans_smiles = "C/C=C/C"  # E configuration
    
    # Create molecules
    cis_mol = Chem.MolFromSmiles(cis_smiles)
    trans_mol = Chem.MolFromSmiles(trans_smiles)
    
    # Add hydrogens (critical for accurate energy/geometry)
    cis_mol = Chem.AddHs(cis_mol)
    trans_mol = Chem.AddHs(trans_mol)
    
    # 2. GENERATE 3D CONFORMERS & OPTIMIZE
    # Embed 3D coordinates
    AllChem.EmbedMolecule(cis_mol, randomSeed=42)
    AllChem.EmbedMolecule(trans_mol, randomSeed=42)
    
    # Optimize geometry using MMFF (Merck Molecular Force Field)
    AllChem.MMFFOptimizeMolecule(cis_mol)
    AllChem.MMFFOptimizeMolecule(trans_mol)
    
    # 3. CALCULATE ENERGIES
    cis_props = AllChem.MMFFGetMoleculeProperties(cis_mol)
    trans_props = AllChem.MMFFGetMoleculeProperties(trans_mol)
    
    cis_ff = AllChem.MMFFGetMoleculeForceField(cis_mol, cis_props)
    trans_ff = AllChem.MMFFGetMoleculeForceField(trans_mol, trans_props)
    
    cis_energy = cis_ff.CalcEnergy()
    trans_energy = trans_ff.CalcEnergy()
    
    # Normalize energies (set lower one to 0 for relative comparison)
    min_energy = min(cis_energy, trans_energy)
    cis_energy_norm = cis_energy - min_energy
    trans_energy_norm = trans_energy - min_energy
    
    # 4. PREPARE DATA FOR VISUALIZATION
    # Convert to SDF format to pass coordinates to JS
    cis_sdf = Chem.MolToMolBlock(cis_mol)
    trans_sdf = Chem.MolToMolBlock(trans_mol)
    
    # Create visualization
    html = create_isomer_viewer(
        cis_sdf, trans_sdf,
        cis_energy_norm, trans_energy_norm
    )
    
    return HTML(html)


def create_isomer_viewer(cis_sdf, trans_sdf, cis_energy, trans_energy):
    """
    Create HTML viewer for geometric isomers using vertical stacked layout
    Includes 3Dmol.js CDN for standalone rendering.
    """
    # Generate unique viewer IDs to allow multiple instances in one notebook
    cis_viewer_id = f"viewer_cis_{np.random.randint(100000, 999999)}"
    trans_viewer_id = f"viewer_trans_{np.random.randint(100000, 999999)}"
    
    # Color scheme
    cis_color = '#2196F3'  # Blue
    cis_bg = '#f0f8ff'     # Light blue
    trans_color = '#9C27B0'  # Purple
    trans_bg = '#f3e5f5'   # Light purple
    
    # Prepare SDF strings for JS injection (escape backticks if necessary)
    cis_sdf_js = cis_sdf.replace('\n', '\\n')
    trans_sdf_js = trans_sdf.replace('\n', '\\n')

    html = f"""
    <script src="https://3Dmol.org/build/3Dmol-min.js"></script>

    <div style="font-family: Arial, sans-serif; max-width: 900px; margin: 20px auto;">
        
        <div style="border: 3px solid {cis_color}; border-radius: 10px; padding: 15px; background: white; margin-bottom: 20px;">
            <div style="text-align: center; margin-bottom: 10px;">
                <h3 style="color: {cis_color}; margin: 0;">Cis-2-Butene (Z-isomer)</h3>
                <p style="color: #666; font-size: 13px; margin: 5px 0;">Methyl groups on the same side of the double bond</p>
            </div>
            
            <div style="display: flex; gap: 20px; align-items: center;">
                <div id="{cis_viewer_id}" class="viewer-container" style="position: relative; width: 100%; height: 300px; flex: 1;"></div>
                <div style="flex: 1; text-align: center;">
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
                        <div style="margin-bottom: 15px;">
                            <div style="font-size: 13px; color: #666; margin-bottom: 5px;">Relative Energy</div>
                            <div style="font-size: 24px; font-weight: bold; color: {cis_color};">{cis_energy:.3f}</div>
                            <div style="font-size: 12px; color: #999;">kcal/mol</div>
                        </div>
                        <div style="border-top: 2px solid #dee2e6; padding-top: 15px;">
                            <div style="font-size: 12px; color: #666; margin-bottom: 8px;">Configuration</div>
                            <div style="font-size: 14px; font-weight: bold; color: #333;">Z (zusammen)</div>
                            <div style="font-size: 11px; color: #999; margin-top: 5px;">High priority groups<br>on same side</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <div style="border: 3px solid {trans_color}; border-radius: 10px; padding: 15px; background: white; margin-bottom: 20px;">
            <div style="text-align: center; margin-bottom: 10px;">
                <h3 style="color: {trans_color}; margin: 0;">Trans-2-Butene (E-isomer)</h3>
                <p style="color: #666; font-size: 13px; margin: 5px 0;">Methyl groups on opposite sides of the double bond</p>
            </div>
            
            <div style="display: flex; gap: 20px; align-items: center;">
                <div id="{trans_viewer_id}" class="viewer-container" style="position: relative; width: 100%; height: 300px; flex: 1;"></div>
                <div style="flex: 1; text-align: center;">
                    <div style="background: #f5f5f5; padding: 20px; border-radius: 8px;">
                        <div style="margin-bottom: 15px;">
                            <div style="font-size: 13px; color: #666; margin-bottom: 5px;">Relative Energy</div>
                            <div style="font-size: 24px; font-weight: bold; color: {trans_color};">{trans_energy:.3f}</div>
                            <div style="font-size: 12px; color: #999;">kcal/mol</div>
                        </div>
                        <div style="border-top: 2px solid #dee2e6; padding-top: 15px;">
                            <div style="font-size: 12px; color: #666; margin-bottom: 8px;">Configuration</div>
                            <div style="font-size: 14px; font-weight: bold; color: #333;">E (entgegen)</div>
                            <div style="font-size: 11px; color: #999; margin-top: 5px;">High priority groups<br>on opposite sides</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <div style="background: #fafafa; padding: 15px; border-radius: 8px; border: 1px solid #dee2e6;">
            <div style="font-size: 13px; color: #333; line-height: 1.6;">
                <strong>Key Observations:</strong><br>
                • Trans isomer is typically more stable (lower energy) due to reduced steric strain [Image of steric strain].<br>
                • The double bond prevents rotation, keeping these as separate, stable molecules<br>
                • Energy barrier for interconversion is ~65 kcal/mol (π-bond breaking required)<br>
                • At physiological temperature, these isomers do not interconvert
            </div>
        </div>
    </div>
    
    <script>
    (function() {{
        // Wait for 3Dmol to load if necessary
        var initViewer = function() {{
            if (typeof $3Dmol === 'undefined') {{
                setTimeout(initViewer, 100);
                return;
            }}
            
            var cisSDF = `{cis_sdf_js}`;
            var transSDF = `{trans_sdf_js}`;
            
            var cisContainer = document.getElementById('{cis_viewer_id}');
            var transContainer = document.getElementById('{trans_viewer_id}');
            
            if (cisContainer && transContainer) {{
                // Initialize CIS viewer
                var cisViewer = $3Dmol.createViewer(cisContainer, {{
                    backgroundColor: '{cis_bg}',
                    disableFog: true
                }});
                
                // Initialize TRANS viewer
                var transViewer = $3Dmol.createViewer(transContainer, {{
                    backgroundColor: '{trans_bg}',
                    disableFog: true
                }});
                
                // Add models
                cisViewer.addModel(cisSDF, 'sdf');
                cisViewer.setStyle({{}}, {{
                    stick: {{radius: 0.15}},
                    sphere: {{scale: 0.3}}
                }});
                cisViewer.zoomTo();
                cisViewer.render();
                
                transViewer.addModel(transSDF, 'sdf');
                transViewer.setStyle({{}}, {{
                    stick: {{radius: 0.15}},
                    sphere: {{scale: 0.3}}
                }});
                transViewer.zoomTo();
                transViewer.render();
                
                // Handle window resize
                window.addEventListener('resize', function() {{
                    cisViewer.resize();
                    cisViewer.render();
                    transViewer.resize();
                    transViewer.render();
                }});
            }}
        }};
        
        initViewer();
    }})();
    </script>
    """
    
    return html

# Run the visualization
create_geometric_isomer_comparison()

Cis-2-Butene (Z-isomer)

Methyl groups on the same side of the double bond

Relative Energy
1.351
kcal/mol
Configuration
Z (zusammen)
High priority groups
on same side

Trans-2-Butene (E-isomer)

Methyl groups on opposite sides of the double bond

Relative Energy
0.000
kcal/mol
Configuration
E (entgegen)
High priority groups
on opposite sides
Key Observations:
• Trans isomer is typically more stable (lower energy) due to reduced steric strain [Image of steric strain].
• The double bond prevents rotation, keeping these as separate, stable molecules
• Energy barrier for interconversion is ~65 kcal/mol (π-bond breaking required)
• At physiological temperature, these isomers do not interconvert

Overcoming the rotational barrier of a double bond requires breaking its \(\pi\)-bond, an energetically demanding process that costs approximately 65–80 kcal/mol. Because this energy barrier is orders of magnitude higher than the thermal kinetic energy available at body temperature (~0.6 kcal/mol), spontaneous rotation is statistically impossible.

This thermodynamic ‘lock’ ensures that geometric isomers remain distinct under physiological conditions, preserving the structural integrity required for enzyme recognition.

Below are examples of simple drugs and their corresponding geometric isomers.

Hide code cell source

import numpy as np
from rdkit import Chem
from rdkit.Chem import AllChem
from IPython.display import HTML
import json
import math

# --- 1. SETUP COLORS & FUNCTIONS (Same as before) ---
COLORS = {
    'molecule': {'primary': '#2196F3', 'background': '#f0f8ff'},
    'neutral': {'text_primary': '#333333', 'text_secondary': '#666666', 'border': '#dee2e6', 'background': '#f5f5f5'},
    'chart': {'marker_current': '#f44336', 'marker_double_break': '#FF9800', 'marker_rotation': '#9C27B0'}
}

def create_butene():
    mol = Chem.MolFromSmiles('CC=CC')
    mol = Chem.AddHs(mol)
    AllChem.EmbedMolecule(mol, randomSeed=42)
    AllChem.MMFFOptimizeMolecule(mol)
    return mol

def mol_to_sdf_block(mol):
    return Chem.MolToMolBlock(mol)

def add_thermal_motion(mol, temperature_factor):
    conf = mol.GetConformer()
    for i in range(mol.GetNumAtoms()):
        pos = conf.GetAtomPosition(i)
        displacement = np.random.randn(3) * temperature_factor * 0.02
        conf.SetAtomPosition(i, [pos.x + displacement[0], pos.y + displacement[1], pos.z + displacement[2]])
    return mol

def break_double_bond(mol):
    editable = Chem.EditableMol(mol)
    double_bond_atoms = None
    for bond in mol.GetBonds():
        if bond.GetBondType() == Chem.BondType.DOUBLE:
            begin_idx = bond.GetBeginAtomIdx()
            end_idx = bond.GetEndAtomIdx()
            double_bond_atoms = (begin_idx, end_idx)
            editable.RemoveBond(begin_idx, end_idx)
            editable.AddBond(begin_idx, end_idx, Chem.BondType.SINGLE)
            break
    new_mol = editable.GetMol()
    Chem.SanitizeMol(new_mol, sanitizeOps=Chem.SanitizeFlags.SANITIZE_ALL^Chem.SanitizeFlags.SANITIZE_KEKULIZE)
    return new_mol, double_bond_atoms

def rotate_around_bond(mol, atom1_idx, atom2_idx, angle_deg):
    conf = mol.GetConformer()
    pos1 = conf.GetAtomPosition(atom1_idx)
    pos2 = conf.GetAtomPosition(atom2_idx)
    axis = np.array([pos2.x - pos1.x, pos2.y - pos1.y, pos2.z - pos1.z])
    axis = axis / np.linalg.norm(axis)
    
    atoms_to_rotate = []
    for atom in mol.GetAtoms():
        if atom.GetIdx() != atom1_idx:
            for neighbor in atom.GetNeighbors():
                if neighbor.GetIdx() == atom2_idx or neighbor.GetIdx() in atoms_to_rotate:
                    if atom.GetIdx() not in atoms_to_rotate and atom.GetIdx() != atom2_idx:
                        atoms_to_rotate.append(atom.GetIdx())
    atoms_to_rotate.append(atom2_idx)
    
    angle_rad = math.radians(angle_deg)
    cos_angle = math.cos(angle_rad)
    sin_angle = math.sin(angle_rad)
    
    for atom_idx in atoms_to_rotate:
        pos = conf.GetAtomPosition(atom_idx)
        p = np.array([pos.x - pos1.x, pos.y - pos1.y, pos.z - pos1.z])
        rotated = p * cos_angle + np.cross(axis, p) * sin_angle + axis * np.dot(axis, p) * (1 - cos_angle)
        conf.SetAtomPosition(atom_idx, (rotated + np.array([pos1.x, pos1.y, pos1.z])).tolist())
    return mol

def break_single_bonds(mol, num_breaks=1):
    editable = Chem.EditableMol(mol)
    single_bonds = []
    for bond in mol.GetBonds():
        if bond.GetBondType() == Chem.BondType.SINGLE:
            b = mol.GetAtomWithIdx(bond.GetBeginAtomIdx())
            e = mol.GetAtomWithIdx(bond.GetEndAtomIdx())
            if b.GetSymbol() == 'C' and e.GetSymbol() == 'C':
                single_bonds.append(bond)
    for bond in single_bonds[:num_breaks]:
        editable.RemoveBond(bond.GetBeginAtomIdx(), bond.GetEndAtomIdx())
    return editable.GetMol()

# --- 2. TRAJECTORY GENERATION (REALISTIC PHYSICS) ---
def generate_temperature_trajectory(num_frames=120):
    trajectory_data = []
    temperatures = []
    bond_status = []
    key_frames = {'double_break': None, 'e_config': None, 'z_config': None, 'single_break': None}
    
    # Phase 1: Warming up (0-600K)
    base_mol = create_butene()
    for i in range(30):
        temp = i * 20  
        mol_copy = add_thermal_motion(Chem.Mol(base_mol), i * 0.05) 
        trajectory_data.append(mol_to_sdf_block(mol_copy))
        temperatures.append(temp)
        bond_status.append("Stable - Thermal Vibration")
    
    # Phase 2: Pi-Bond Break (~650K)
    broken_double, bond_atoms = break_double_bond(base_mol)
    key_frames['double_break'] = 30
    for i in range(1):
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(Chem.Mol(broken_double), 1.5)))
        temperatures.append(650)
        bond_status.append("⚠️ Pi-Bond Breaking")
    
    # Phase 3: Isomerization (650K-850K)
    key_frames['e_config'] = 31
    for i in range(19):
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(Chem.Mol(broken_double), 1.5 + i*0.05)))
        temperatures.append(650 + i * 10)
        bond_status.append("Isomerization (Rotation Barrier)")
    
    # Phase 4: E to Z (850K-1050K)
    for i in range(20):
        rotation_angle = (i / 19) * 180
        mol_copy = Chem.Mol(broken_double)
        if bond_atoms: mol_copy = rotate_around_bond(mol_copy, bond_atoms[0], bond_atoms[1], rotation_angle)
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(mol_copy, 2.5)))
        temperatures.append(840 + i * 10)
        if i == 10: key_frames['z_config'] = 60
        bond_status.append(f"🔄 E → Z Rotation ({rotation_angle:.0f}°)")
    
    # Phase 5: Z-Isomer Heating (1050K-1200K)
    z_config = Chem.Mol(broken_double)
    if bond_atoms: z_config = rotate_around_bond(z_config, bond_atoms[0], bond_atoms[1], 180)
    for i in range(15):
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(Chem.Mol(z_config), 3.0 + i*0.05)))
        temperatures.append(1040 + i * 10)
        bond_status.append("Z-Isomer (High Thermal Stress)")
    
    # Phase 6 & 7: Pyrolysis / Fragmentation (>1200K)
    key_frames['single_break'] = 85
    broken_single = break_single_bonds(z_config, 1)
    for i in range(15):
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(Chem.Mol(broken_single), 4.0 + i*0.1)))
        temperatures.append(1200 + i * 20)
        bond_status.append("⚠️ HOMOLYSIS (Pyrolysis)")
        
    broken_single_2 = break_single_bonds(z_config, 2)
    for i in range(20):
        trajectory_data.append(mol_to_sdf_block(add_thermal_motion(Chem.Mol(broken_single_2), 5.5 + i*0.12)))
        temperatures.append(1500 + i * 25)
        bond_status.append("Complete Dissociation")

    return "$$$$\n".join(trajectory_data) + "$$$$\n", temperatures, bond_status, key_frames

# --- 3. HTML VIEWER ---
def create_temperature_viewer(width=900):
    trajectory_sdf, temperatures, bond_status, key_frames = generate_temperature_trajectory()
    
    # Scale energies for visualization (approx Activation Energy in kcal/mol)
    energies = [t * 0.06 for t in temperatures]
    
    # Random IDs for HTML elements
    ids = {k: f"{k}_{np.random.randint(1e5, 9e5)}" for k in ['viewer', 'slider', 'temp', 'energy', 'status', 'canvas']}
    
    html = f"""
    <div style="font-family: Arial, sans-serif; max-width: {width}px; margin: 20px auto;">
        <div style="border: 3px solid {COLORS['molecule']['primary']}; border-radius: 10px; padding: 15px; background: white;">
            <div style="text-align: center; margin-bottom: 10px;">
                <h3 style="color: {COLORS['molecule']['primary']}; margin: 0;">Pyrolysis Simulation: Temperature-Dependent Bond Breaking</h3>
                <p style="color: {COLORS['neutral']['text_secondary']}; font-size: 13px; margin: 5px 0;">But-2-ene (CH₃-CH=CH-CH₃)</p>
            </div>
            
            <div style="display: flex; gap: 20px; align-items: center;">
                <div id="{ids['viewer']}" class="viewer-container" style="flex: 1; position: relative; width: 100%; height: 300px; overflow: hidden;"></div>
                <canvas id="{ids['canvas']}" width="450" height="250" style="flex: 1;"></canvas>
            </div>
            
            <div style="background: {COLORS['neutral']['background']}; padding: 15px; border-radius: 8px; margin-top: 15px;">
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
                    <label style="font-weight: bold; font-size: 14px;">
                        Temperature: <span id="{ids['temp']}" style="color: {COLORS['molecule']['primary']};">0</span> K
                    </label>
                    <div>
                        <strong>Est. Accessible Ea:</strong> 
                        <span id="{ids['energy']}" style="font-size: 16px; color: {COLORS['molecule']['primary']};">0.0</span> kcal/mol
                    </div>
                </div>
                <input type="range" id="{ids['slider']}" min="0" max="119" value="0" style="width: 100%; height: 8px; cursor: pointer;">
                <div style="margin-top: 10px; padding: 10px; background: white; border-radius: 5px; text-align: center;">
                    <strong>Status:</strong> <span id="{ids['status']}" style="color: {COLORS['molecule']['primary']};">Intact</span>
                </div>
            </div>
        </div>
    </div>
    
    <script>
    (function() {{
        setTimeout(function() {{
            const container = document.getElementById('{ids['viewer']}');
            const viewer = $3Dmol.createViewer(container, {{
                backgroundColor: '{COLORS['molecule']['background']}',
                disableFog: true
            }});
            
            const trajectory = `{trajectory_sdf}`;
            const temperatures = {json.dumps(temperatures)};
            const energies = {json.dumps(energies)};
            const bondStatus = {json.dumps(bond_status)};
            const keyFrames = {json.dumps(key_frames)};
            
            viewer.addModelsAsFrames(trajectory, 'sdf');
            viewer.setStyle({{}}, {{stick: {{radius: 0.15}}, sphere: {{scale: 0.3}} }});
            viewer.zoomTo();
            viewer.render();
            
            const slider = document.getElementById('{ids['slider']}');
            const tempDisplay = document.getElementById('{ids['temp']}');
            const energyDisplay = document.getElementById('{ids['energy']}');
            const statusDisplay = document.getElementById('{ids['status']}');
            const canvas = document.getElementById('{ids['canvas']}');
            const ctx = canvas.getContext('2d');
            
            let currentFrame = 0;
            
            function drawChart() {{
                const padding = {{left: 60, right: 20, top: 35, bottom: 50}};
                const chartWidth = canvas.width - padding.left - padding.right;
                const chartHeight = canvas.height - padding.top - padding.bottom;
                const maxEnergy = Math.max(...energies);
                
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.fillStyle = '#fafafa'; ctx.fillRect(0, 0, canvas.width, canvas.height);
                
                // Axes
                ctx.strokeStyle = '#333'; ctx.lineWidth = 2;
                ctx.beginPath(); 
                ctx.moveTo(padding.left, padding.top); 
                ctx.lineTo(padding.left, padding.top + chartHeight); 
                ctx.lineTo(padding.left + chartWidth, padding.top + chartHeight); 
                ctx.stroke();
                
                // Curve
                ctx.strokeStyle = '{COLORS['molecule']['primary']}'; ctx.lineWidth = 2.5;
                ctx.beginPath();
                for (let i = 0; i < energies.length; i++) {{
                    const x = padding.left + (i / (energies.length - 1)) * chartWidth;
                    const y = padding.top + chartHeight - (energies[i] / maxEnergy) * chartHeight;
                    if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
                }}
                ctx.stroke();
                
                // Markers
                const drawMarker = (frame, color, radius) => {{
                    if (frame === null) return;
                    const x = padding.left + (frame / (energies.length - 1)) * chartWidth;
                    const y = padding.top + chartHeight - (energies[frame] / maxEnergy) * chartHeight;
                    ctx.fillStyle = color;
                    ctx.beginPath(); ctx.arc(x, y, radius, 0, 2 * Math.PI); ctx.fill(); 
                    ctx.strokeStyle = 'white'; ctx.stroke();
                }};
                
                drawMarker(keyFrames.double_break, '{COLORS['chart']['marker_double_break']}', 8);
                drawMarker(keyFrames.e_config, '{COLORS['chart']['marker_rotation']}', 6);
                drawMarker(keyFrames.z_config, '{COLORS['chart']['marker_rotation']}', 6);
                drawMarker(currentFrame, '{COLORS['chart']['marker_current']}', 6);
                
                // Labels
                ctx.fillStyle = '#333'; ctx.font = 'bold 12px Arial'; ctx.textAlign = 'center';
                ctx.fillText('Energy / Reaction Coordinate', canvas.width/2, 20);
                
                // X-axis Text
                const tSteps = [0, 500, 1000, 1500];
                for (let t of tSteps) {{
                    const idx = temperatures.findIndex(val => val >= t);
                    if (idx !== -1) {{
                        const x = padding.left + (idx / (energies.length - 1)) * chartWidth;
                        ctx.fillText(t + 'K', x, padding.top + chartHeight + 20);
                    }}
                }}
                
                // Y-axis Text
                ctx.textAlign = 'right';
                for (let i=0; i<=4; i++) {{
                   const val = maxEnergy * (1 - i/4);
                   ctx.fillText(val.toFixed(0), padding.left - 10, padding.top + (chartHeight/4)*i + 4);
                }}
            }}
            
            function updateDisplay() {{
                viewer.setFrame(currentFrame);
                viewer.render();
                tempDisplay.textContent = temperatures[currentFrame].toFixed(0);
                energyDisplay.textContent = energies[currentFrame].toFixed(1);
                statusDisplay.textContent = bondStatus[currentFrame];
                drawChart();
            }}
            
            slider.addEventListener('input', function() {{
                currentFrame = parseInt(this.value);
                updateDisplay();
            }});
            
            window.addEventListener('resize', function() {{ viewer.resize(); viewer.render(); }});
            updateDisplay();
            
        }}, 100);
    }})();
    </script>
    """
    return HTML(html)

create_temperature_viewer()

Pyrolysis Simulation: Temperature-Dependent Bond Breaking

But-2-ene (CH₃-CH=CH-CH₃)

Est. Accessible Ea: 0.0 kcal/mol
Status: Intact

Below are examples of simple drugs and their corresponding geometric isomers.

Hide code cell source

from rdkit import Chem
from rdkit.Chem import AllChem, Descriptors
import numpy as np
from IPython.display import HTML
import json
import uuid

# Color scheme for consistency
COLORS = {
    'cis': {
        'primary': '#2196F3',
        'background': '#f0f8ff',
    },
    'trans': {
        'primary': '#9C27B0',
        'background': '#f3e5f5',
    },
}

def create_molecule_from_smiles(smiles, name):
    """Create and prepare molecule with 3D coordinates"""
    mol = Chem.MolFromSmiles(smiles)
    if mol is None:
        return None
    mol = Chem.AddHs(mol)
    AllChem.EmbedMolecule(mol, randomSeed=42)
    AllChem.MMFFOptimizeMolecule(mol, maxIters=500)
    return mol

def mol_to_sdf(mol):
    """Convert molecule to SDF format for proper bond visualization"""
    return Chem.MolToMolBlock(mol)

def create_geometric_isomer_viewer():
    """
    Create interactive viewer for four drug molecules with geometric isomers.
    Shows cis (Z) and trans (E) forms with selector interface.
    """
    
    # Define four drug molecules with geometric isomers
    molecules = {
        'Tamoxifen': {
            'description': 'Breast cancer SERM - E-isomer is therapeutically active',
            'cis_smiles': 'CC/C(=C(/c1ccccc1)\c1ccc(OCCN(C)C)cc1)/c1ccccc1',
            'trans_smiles': 'CC/C(=C(\c1ccccc1)/c1ccc(OCCN(C)C)cc1)/c1ccccc1',
            'cis_desc': """
                <b>Cis (Z) Configuration:</b><br>
                In Z-tamoxifen, the bulky phenyl groups are on the same side of the double bond, 
                creating a more compact, bent molecular shape. This geometry significantly reduces 
                binding affinity to the estrogen receptor, making it much less active therapeutically.
            """,
            'trans_desc': """
                <b>Trans (E) Configuration:</b><br>
                E-tamoxifen has phenyl groups on opposite sides, creating an extended conformation 
                that fits optimally into the estrogen receptor binding pocket. This is the active 
                pharmaceutical ingredient used to treat and prevent breast cancer.
            """,
            'clinical_note': """
                <b>Clinical Significance:</b> Only the E-isomer is marketed as the active drug. 
                The difference in geometry explains why one isomer is therapeutic while the other is essentially inactive.
            """
        },
        'Clomiphene': {
            'description': 'Fertility drug - marketed as mixture of both geometric isomers',
            'cis_smiles': 'CCN(CC)CCO/C(=C(/c1ccccc1)\c1ccc(Cl)cc1)/c1ccccc1',
            'trans_smiles': 'CCN(CC)CCO/C(=C(\c1ccccc1)/c1ccc(Cl)cc1)/c1ccccc1',
            'cis_desc': """
                <b>Cis (Z) Configuration - Zuclomiphene:</b><br>
                The Z-isomer (zuclomiphene) has a longer biological half-life (up to 30 days) and 
                can accumulate with repeated doses. It contributes to the overall therapeutic effect 
                but is less potent as an estrogen antagonist.
            """,
            'trans_desc': """
                <b>Trans (E) Configuration - Enclomiphene:</b><br>
                The E-isomer (enclomiphene) is the more potent estrogen receptor antagonist with a 
                shorter half-life. It is the primary active component for inducing ovulation in 
                fertility treatment.
            """,
            'clinical_note': """
                <b>Clinical Significance:</b> Marketed as a 62:38 mixture of Z:E isomers. The different 
                pharmacokinetic profiles of the two isomers contribute to the drug's overall efficacy and side effect profile.
            """
        },
        'Diethylstilbestrol (DES)': {
            'description': 'Synthetic estrogen - trans form mimics natural estradiol geometry',
            'cis_smiles': 'CC/C(=C(/CC)\c1ccc(O)cc1)/c1ccc(O)cc1',
            'trans_smiles': 'CC/C(=C(\CC)/c1ccc(O)cc1)/c1ccc(O)cc1',
            'cis_desc': """
                <b>Cis (Z) Configuration:</b><br>
                The Z-isomer positions the two hydroxyl-bearing phenyl rings on the same side, 
                creating steric clashing and preventing the molecule from adopting an estradiol-like 
                conformation. This results in significantly reduced estrogenic activity.
            """,
            'trans_desc': """
                <b>Trans (E) Configuration:</b><br>
                The E-isomer allows the hydroxyl groups to be spaced similarly to natural estradiol's 
                hydroxyl groups. This geometric arrangement enables optimal binding to the estrogen 
                receptor, making trans-DES a potent synthetic estrogen.
            """,
            'clinical_note': """
                <b>Clinical Significance:</b> The trans form's ability to mimic estradiol geometry 
                explains its high estrogenic potency. Historically used during pregnancy (now contraindicated), 
                illustrating how molecular shape determines hormone activity.
            """
        },
        'Combretastatin A-4': {
            'description': 'Antitumor agent - cis form disrupts tumor vasculature',
            'cis_smiles': 'COc1ccc(/C=C\c2cc(OC)c(OC)c(OC)c2)cc1O',
            'trans_smiles': 'COc1ccc(/C=C/c2cc(OC)c(OC)c(OC)c2)cc1O',
            'cis_desc': """
                <b>Cis (Z) Configuration:</b><br>
                The Z-isomer is the naturally occurring and biologically active form. The bent 
                conformation allows it to bind to tubulin and disrupt microtubule assembly, 
                selectively damaging tumor blood vessel endothelial cells.
            """,
            'trans_desc': """
                <b>Trans (E) Configuration:</b><br>
                The E-isomer has a more linear extended structure that does not fit properly into 
                the tubulin binding site. It shows significantly reduced or no antitumor activity. 
                The molecule can photoisomerize from cis to trans upon light exposure.
            """,
            'clinical_note': """
                <b>Clinical Significance:</b> Requires storage protection from light to prevent 
                photoisomerization to the inactive trans form. A unique case where the cis isomer 
                is therapeutic - most drugs favor trans configurations.
            """
        }
    }
    
    # Generate 3D structures and SDF data
    mol_data_library = {}
    
    for name, data in molecules.items():
        cis_mol = create_molecule_from_smiles(data['cis_smiles'], f"{name}-cis")
        trans_mol = create_molecule_from_smiles(data['trans_smiles'], f"{name}-trans")
        
        if cis_mol is None or trans_mol is None:
            continue
        
        cis_sdf = mol_to_sdf(cis_mol)
        trans_sdf = mol_to_sdf(trans_mol)
        
        cis_mw = Descriptors.MolWt(Chem.RemoveHs(cis_mol))
        cis_logp = Descriptors.MolLogP(Chem.RemoveHs(cis_mol))
        trans_mw = Descriptors.MolWt(Chem.RemoveHs(trans_mol))
        trans_logp = Descriptors.MolLogP(Chem.RemoveHs(trans_mol))
        
        mol_data_library[name] = {
            'description': data['description'],
            'cis_sdf': cis_sdf,
            'trans_sdf': trans_sdf,
            'cis_mw': f"{cis_mw:.1f}",
            'cis_logp': f"{cis_logp:.2f}",
            'trans_mw': f"{trans_mw:.1f}",
            'trans_logp': f"{trans_logp:.2f}",
            'cis_desc': data['cis_desc'],
            'trans_desc': data['trans_desc'],
            'clinical_note': data['clinical_note']
        }
    
    # Generate unique IDs
    unique_id = str(uuid.uuid4())[:8]
    cis_viewer_id = f"cis_viewer_{unique_id}"
    trans_viewer_id = f"trans_viewer_{unique_id}"
    cis_desc_id = f"cis_desc_{unique_id}"
    trans_desc_id = f"trans_desc_{unique_id}"
    clinical_note_id = f"clinical_{unique_id}"
    
    # Serialize data for JavaScript
    mol_json = json.dumps(mol_data_library)
    
    # Create selector buttons
    buttons_html = ""
    for name in molecules.keys():
        buttons_html += (
            f'<button onclick="window.showMolecule_{unique_id}(\'{name}\')" '
            f'style="display:block; width:100%; margin: 6px 0; padding: 12px; '
            f'text-align:left; background: #ffffff; border: 1px solid #ddd; '
            f'border-radius: 6px; cursor: pointer; font-weight:600; color: #333; transition: 0.2s;">'
            f'{name}</button>'
        )
    
    html_code = f"""
    <script src="https://3Dmol.csb.pitt.edu/build/3Dmol-min.js"></script>
    <style>
        .viewer-container {{
            position: relative !important;
            width: 100% !important;
            height: 280px !important;
            margin: 0 !important;
            padding: 0 !important;
            overflow: hidden !important;
            border-radius: 8px;
        }}
        .viewer-container canvas {{
            position: absolute !important;
            top: 0 !important;
            left: 0 !important;
            width: 100% !important;
            height: 100% !important;
        }}
        button:hover {{
            background-color: #f0f0f0 !important;
            border-color: #2196F3 !important;
        }}
    </style>
    
    <div style="font-family: 'Segoe UI', sans-serif; max-width: 1000px; margin: 20px auto;">
        <h2 style="text-align: center; color: #333; margin-bottom: 10px;">Geometric Isomers in Drug Molecules</h2>
        <p style="text-align: center; color: #666; margin-bottom: 25px; font-size: 14px;">
            Select a drug to explore how cis (Z) and trans (E) configurations affect biological activity
        </p>
        
        <div style="display: flex; border: 1px solid #ccc; border-radius: 8px; overflow: hidden; background-color: #f9f9f9;">
            
            <!-- Left Sidebar: Selector -->
            <div style="width: 22%; background-color: #f0f2f5; padding: 15px; border-right: 1px solid #ddd;">
                <h4 style="margin-top:0; color: #444; font-size: 15px;">Select Drug Molecule</h4>
                {buttons_html}
            </div>
            
            <!-- Right Content Area -->
            <div style="width: 78%; padding: 20px;">
                
                <!-- Cis Isomer Section -->
                <div style="border: 3px solid {COLORS['cis']['primary']}; border-radius: 10px; padding: 15px; background: white; margin-bottom: 20px;">
                    <div style="text-align: center; margin-bottom: 12px;">
                        <h4 style="color: {COLORS['cis']['primary']}; margin: 0 0 5px 0;">Cis (Z) Isomer</h4>
                        <div id="cis_props_{unique_id}" style="color: #666; font-size: 12px;"></div>
                    </div>
                    
                    <div id="{cis_desc_id}" style="background-color: #e3f2fd; border-left: 4px solid {COLORS['cis']['primary']}; padding: 12px; border-radius: 4px; color: #0d47a1; margin-bottom: 12px; font-size: 13px;">
                        Select a drug to begin.
                    </div>
                    
                    <div id="{cis_viewer_id}" class="viewer-container" style="background: {COLORS['cis']['background']};"></div>
                </div>
                
                <!-- Trans Isomer Section -->
                <div style="border: 3px solid {COLORS['trans']['primary']}; border-radius: 10px; padding: 15px; background: white; margin-bottom: 20px;">
                    <div style="text-align: center; margin-bottom: 12px;">
                        <h4 style="color: {COLORS['trans']['primary']}; margin: 0 0 5px 0;">Trans (E) Isomer</h4>
                        <div id="trans_props_{unique_id}" style="color: #666; font-size: 12px;"></div>
                    </div>
                    
                    <div id="{trans_desc_id}" style="background-color: #f3e5f5; border-left: 4px solid {COLORS['trans']['primary']}; padding: 12px; border-radius: 4px; color: #4a148c; margin-bottom: 12px; font-size: 13px;">
                        Select a drug to begin.
                    </div>
                    
                    <div id="{trans_viewer_id}" class="viewer-container" style="background: {COLORS['trans']['background']};"></div>
                </div>
                
                <!-- Clinical Note -->
                <div id="{clinical_note_id}" style="background-color: #e8f5e9; border-left: 4px solid #4caf50; padding: 15px; border-radius: 4px; color: #1b5e20; font-size: 13px;">
                </div>
                
            </div>
        </div>
        
        <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 20px;">
            <h4 style="color: #333; margin-top: 0; font-size: 14px;">How to Use:</h4>
            <ul style="color: #666; line-height: 1.7; font-size: 13px; margin: 0;">
                <li><strong>Select:</strong> Click a drug name in the left panel</li>
                <li><strong>Rotate:</strong> Click and drag on molecules to rotate in 3D</li>
                <li><strong>Compare:</strong> Observe spatial differences between cis and trans forms</li>
                <li><strong>Learn:</strong> Read how geometry affects biological activity</li>
            </ul>
        </div>
    </div>
    
    <script>
    (function() {{
        const molData = {mol_json};
        
        let viewer_cis = null;
        let viewer_trans = null;
        
        window.showMolecule_{unique_id} = function(name) {{
            const data = molData[name];
            if (!data) return;
            
            // Update descriptions
            document.getElementById('{cis_desc_id}').innerHTML = data.cis_desc;
            document.getElementById('{trans_desc_id}').innerHTML = data.trans_desc;
            document.getElementById('{clinical_note_id}').innerHTML = data.clinical_note;
            document.getElementById('cis_props_{unique_id}').innerHTML = 
                `MW: ${{data.cis_mw}} g/mol | LogP: ${{data.cis_logp}}`;
            document.getElementById('trans_props_{unique_id}').innerHTML = 
                `MW: ${{data.trans_mw}} g/mol | LogP: ${{data.trans_logp}}`;
            
            // Clear and recreate viewers
            const cisContainer = document.getElementById('{cis_viewer_id}');
            const transContainer = document.getElementById('{trans_viewer_id}');
            
            cisContainer.innerHTML = '';
            transContainer.innerHTML = '';
            
            // Create cis viewer
            viewer_cis = $3Dmol.createViewer(cisContainer, {{
                backgroundColor: '{COLORS['cis']['background']}',
                disableFog: true
            }});
            viewer_cis.addModel(data.cis_sdf, "sdf");
            viewer_cis.setStyle({{}}, {{
                stick: {{radius: 0.15, color: 'spectrum'}},
                sphere: {{scale: 0.25}}
            }});
            viewer_cis.zoomTo();
            viewer_cis.render();
            
            // Create trans viewer
            viewer_trans = $3Dmol.createViewer(transContainer, {{
                backgroundColor: '{COLORS['trans']['background']}',
                disableFog: true
            }});
            viewer_trans.addModel(data.trans_sdf, "sdf");
            viewer_trans.setStyle({{}}, {{
                stick: {{radius: 0.15, color: 'spectrum'}},
                sphere: {{scale: 0.25}}
            }});
            viewer_trans.zoomTo();
            viewer_trans.render();
        }};
        
        // Handle window resize
        window.addEventListener('resize', function() {{
            if (viewer_cis) {{
                viewer_cis.resize();
                viewer_cis.render();
            }}
            if (viewer_trans) {{
                viewer_trans.resize();
                viewer_trans.render();
            }}
        }});
        
        // Initialize with first molecule
        setTimeout(function() {{
            const firstKey = Object.keys(molData)[0];
            if (firstKey) window.showMolecule_{unique_id}(firstKey);
        }}, 100);
    }})();
    </script>
    """
    
    return HTML(html_code)

# Run the visualization
create_geometric_isomer_viewer()

Geometric Isomers in Drug Molecules

Select a drug to explore how cis (Z) and trans (E) configurations affect biological activity

Select Drug Molecule

Cis (Z) Isomer

Select a drug to begin.

Trans (E) Isomer

Select a drug to begin.

How to Use:

  • Select: Click a drug name in the left panel
  • Rotate: Click and drag on molecules to rotate in 3D
  • Compare: Observe spatial differences between cis and trans forms
  • Learn: Read how geometry affects biological activity

4. Summary and Key Takeaways#

In this section, we’ve explored that molecules can show geometric differences even if they have the same group of atoms. This is dependent on how the atoms are arranged in the molecules.

  • Geometric isomers arise from restricted rotation around double bonds or within ring systems, creating distinct spatial arrangements that cannot interconvert at physiological temperatures.

  • Biological activity depends on three-dimensional shape, meaning cis and trans isomers often have dramatically different potencies, selectivities, or safety profiles—one may be therapeutic while the other is inactive or toxic.

Understanding these principles is essential for designing effective medicines.