Computational Demo: Harmonic oscillator

Computational Demo: Harmonic oscillator#

import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, Dropdown, FloatSlider
# Physical constants
h = 6.62607015e-34        # Planck constant (J s)
hbar = h / (2 * np.pi)
c = 2.99792458e10         # speed of light (cm / s)

# Atomic masses in kg
atomic_masses = {
    "H": 1.6735575e-27,
    "C": 1.9944235e-26,
    "N": 2.3258671e-26,
    "O": 2.6566962e-26,
    "F": 3.1547626e-26
}
def reduced_mass(m1, m2):
    return (m1 * m2) / (m1 + m2)

def frequency(k, mu):
    return (1 / (2 * np.pi)) * np.sqrt(k / mu)

def energy_levels(n, nu):
    return h * nu * (n + 0.5)

def turning_point(E, k):
    return np.sqrt(2 * E / k)
def harmonic_oscillator_plot(atom1, atom2, k):
    # Masses and reduced mass
    m1 = atomic_masses[atom1]
    m2 = atomic_masses[atom2]
    mu = reduced_mass(m1, m2)
    
    # Frequency
    nu = frequency(k, mu)
    nu_cm = nu / c
    
    # Energy levels
    v_levels = np.arange(4)
    energies = energy_levels(v_levels, nu)
    
    # Position grid (set by highest turning point)
    x_max = turning_point(energies[-1], k) * 1.3
    x = np.linspace(-x_max, x_max, 1000)
    
    # Harmonic potential
    V = 0.5 * k * x**2
    
    # Plot
    plt.figure(figsize=(8,6))
    plt.plot(x * 1e10, V, label="Harmonic Potential", color="black")
    
    # Plot energy levels and turning points
    for v, E in zip(v_levels, energies):
        xtp = turning_point(E, k)
        plt.hlines(E, -xtp*1e10, xtp*1e10, linestyles="dashed")
        plt.text(xtp*1e10*1.05, E, f"$v={v}$", verticalalignment="center")
    
    # Fundamental transition arrow
    plt.annotate(
        "",
        xy=(0, energies[1]),
        xytext=(0, energies[0]),
        arrowprops=dict(arrowstyle="<->", linewidth=2)
    )
    plt.text(0.1, 0.5*(energies[0]+energies[1]), "Fundamental\nTransition")
    
    # Labels
    plt.xlabel("Displacement $x$ (Å)")
    plt.ylabel("Energy (J)")
    plt.title(f"Harmonic Oscillator: {atom1}-{atom2}")
    plt.legend()
    plt.grid(True)
    plt.show()
    
    # Print frequencies
    print(f"Reduced mass μ = {mu:.3e} kg")
    print(f"Fundamental frequency ν = {nu:.3e} Hz")
    print(f"Fundamental frequency = {nu_cm:.1f} cm⁻¹")
interact(
    harmonic_oscillator_plot,
    atom1=Dropdown(options=atomic_masses.keys(), value="H", description="Atom 1"),
    atom2=Dropdown(options=atomic_masses.keys(), value="F", description="Atom 2"),
    k=FloatSlider(
        value=1000,
        min=500,
        max=1500,
        step=50,
        description="Force constant (N/m)",
        continuous_update=True
    )
);