Source code for chemistrykit.quantum.systems.rigid_rotor

r"""The rigid-rotor model of molecular rotation.

See Atkins & de Paula, *Physical Chemistry*, 11th ed., Ch. 7.5 (rotational
motion) and Ch. 12.2 (rotational spectroscopy), or Levine, *Quantum
Chemistry*, 7th ed., Ch. 6.4.
"""

from __future__ import annotations

import numpy as np

from chemistrykit.constants import HBAR
from chemistrykit.quantum.core.base_system import QuantumSystem

__all__ = ["RigidRotor"]


[docs] class RigidRotor(QuantumSystem): r"""A linear rigid rotor of moment of inertia `I`. .. math:: E_J = J(J+1)\frac{\hbar^2}{2I}, \qquad g_J = 2J+1, \qquad J=0,1,2,\dots (Atkins & de Paula, *Physical Chemistry*, 11th ed., eq. 7.20-7.21.) Each level is :math:`(2J+1)`-fold degenerate (one state per value of the projection quantum number :math:`M_J=-J,\dots,J`), and allowed microwave (pure rotational) transitions follow the selection rule :math:`\Delta J=\pm1` (Atkins & de Paula, *Physical Chemistry*, 11th ed., Ch. 12.2), giving evenly spaced absorption lines at :math:`2B(J+1)` where :math:`B=\hbar^2/(2I)` is the rotational constant. Parameters ---------- moment_of_inertia : float Moment of inertia `I`, in kg m^2. Examples -------- >>> rotor = RigidRotor(moment_of_inertia=1.45e-46) # ~HCl-like >>> rotor.degeneracy(0) 1 >>> rotor.degeneracy(2) 5 """ def __init__(self, moment_of_inertia: float): if moment_of_inertia <= 0: raise ValueError("moment_of_inertia must be positive") self.moment_of_inertia = float(moment_of_inertia)
[docs] @classmethod def from_diatomic(cls, mass1: float, mass2: float, bond_length: float) -> RigidRotor: r"""Build a :class:`RigidRotor` from a diatomic's atomic masses and bond length. Uses the reduced mass :math:`\mu=m_1m_2/(m_1+m_2)` and :math:`I=\mu r^2` (Atkins & de Paula, *Physical Chemistry*, 11th ed., eq. 7.19). Parameters ---------- mass1, mass2 : float Atomic masses, in kg. bond_length : float Bond length `r`, in m. Returns ------- RigidRotor Examples -------- >>> import scipy.constants as sc >>> rotor = RigidRotor.from_diatomic(mass1=1.008 * sc.atomic_mass, mass2=34.97 * sc.atomic_mass, bond_length=127.5e-12) # HCl >>> bool(1.0e-47 < rotor.moment_of_inertia < 5.0e-47) True """ mu = mass1 * mass2 / (mass1 + mass2) return cls(moment_of_inertia=mu * bond_length**2)
@property def rotational_constant(self) -> float: r"""float: The rotational constant :math:`B=\hbar^2/(2I)`, in J.""" return HBAR**2 / (2.0 * self.moment_of_inertia)
[docs] def energy(self, J): r"""Return :math:`E_J=J(J+1)\hbar^2/(2I)`. Parameters ---------- J : int or array-like of int Rotational quantum number(s), each >= 0. Returns ------- float or ndarray Energy, in J. """ J = np.asarray(J, dtype=np.float64) if np.any(J < 0): raise ValueError("J must be >= 0") return J * (J + 1.0) * self.rotational_constant
[docs] def degeneracy(self, J: int) -> int: """Return the degeneracy :math:`g_J=2J+1` of level `J`. Parameters ---------- J : int Returns ------- int """ if J < 0: raise ValueError("J must be >= 0") return 2 * J + 1
[docs] def transition_energy(self, J: int) -> float: r"""Return the :math:`J\to J+1` absorption transition energy, :math:`2B(J+1)`. Parameters ---------- J : int Initial (lower) rotational quantum number, >= 0. Returns ------- float Energy, in J. Examples -------- Successive rotational-spectrum lines are evenly spaced by :math:`2B` -- the textbook rigid-rotor selection-rule result: >>> rotor = RigidRotor(moment_of_inertia=1.45e-46) >>> spacing1 = rotor.transition_energy(1) - rotor.transition_energy(0) >>> spacing2 = rotor.transition_energy(2) - rotor.transition_energy(1) >>> bool(abs(spacing1 - spacing2) < 1e-30) True """ if J < 0: raise ValueError("J must be >= 0") return float(self.energy(J + 1) - self.energy(J))