codex/exciton-binding-optical-transition.py

exciton-binding-optical-transition.py
"""Hypothetical 3D hydrogenic exciton positions, not an empirical spectrum.

Tested with Python 3.14, matplotlib 3.10.7, numpy 2.3.5. Run from _media;
write the matching PNG to CWD. Equal sticks show positions only. The assumed
optical transitions are permitted; no oscillator strengths are calculated.
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    # SI values used only to calculate this hypothetical model.
    e = 1.602176634e-19
    hbar = 1.054571817e-34
    epsilon0 = 8.8541878128e-12
    m0 = 9.1093837015e-31
    me, mh, epsilon_r, gap_eV = 0.10 * m0, 0.40 * m0, 10.0, 1.500
    mu = me * mh / (me + mh)
    coulomb = e**2 / (4 * np.pi * epsilon0 * epsilon_r)
    radius_nm = hbar**2 / (mu * coulomb) * 1e9
    rydberg_meV = mu * coulomb**2 / (2 * hbar**2) / e * 1e3
    n = np.arange(1, 4)
    internal_meV = -rydberg_meV / n**2
    optical_eV = gap_eV + internal_meV / 1e3
    assert np.all(internal_meV < 0) and np.all(optical_eV < gap_eV)
    assert np.all(np.diff(optical_eV) > 0)
    assert np.isclose(rydberg_meV, 10.88455, rtol=1e-6)
    assert np.isclose(radius_nm, 6.61472, rtol=1e-6)

    plt.rcParams.update({"font.size": 11, "font.family": "DejaVu Sans"})
    fig, (left, right) = plt.subplots(1, 2, figsize=(10, 5.8), facecolor="white")
    fig.subplots_adjust(left=0.08, right=0.97, bottom=0.28, top=0.78, wspace=0.48)

    left.set(xlim=(0, 1.22), ylim=(-12.1, 2), xticks=[],
             ylabel="Internal energy relative to continuum (meV)")
    left.set_title("Binding within the pair", fontweight="bold", pad=15)
    left.axhspan(0, 2, color="#eaf0f5")
    left.axhline(0, color="#555555", linestyle="--")
    left.text(0.03, 0.6, "Unbound continuum", fontsize=10)
    for ni, value in zip(n, internal_meV, strict=True):
        left.hlines(value, 0.10, 0.57, color="#246bb1", lw=2.8)
        left.text(0.62, value, f"n = {ni}: {value:.3f}", va="center", fontsize=10)
    left.annotate("", xy=(0.055, 0), xytext=(0.055, internal_meV[0]),
                  arrowprops={"arrowstyle": "<->", "color": "#c45e16", "lw": 2})
    left.text(0.13, -6.4, f"Separation cost\n{rydberg_meV:.3f} meV",
              color="#994810", fontsize=10)

    right.set(xlim=(-12.1, 2), ylim=(0, 1.37), yticks=[],
              xlabel=r"Photon energy offset $\hbar\omega-E_g$ (meV)")
    right.set_title("Optical creation from the crystal ground state",
                    fontweight="bold", fontsize=10, pad=15)
    right.axvspan(0, 2, color="#eaf0f5")
    right.axvline(0, color="#555555", linestyle="--")
    right.hlines(0, -12.1, 2, color="#555555", lw=1)
    for ni, position in zip(n, internal_meV, strict=True):
        right.vlines(position, 0, 0.90, color="#246bb1", lw=2.8)
    for ni, position, y in zip(n, internal_meV, [1.02, 1.20, 1.02], strict=True):
        right.text(position, y, f"n = {ni}", ha="center", fontsize=10)
    right.text(1.0, 0.46, "Unbound threshold", rotation=90, ha="center", fontsize=10)

    for ax in (left, right):
        ax.spines[["top", "right"]].set_visible(False)
    fig.suptitle("Exciton binding lowers the optical creation energy", fontsize=15, y=0.96)
    fig.text(0.5, 0.87, r"Hypothetical 3D model: $m_e^*=0.10m_0$, $m_h^*=0.40m_0$, "
             r"$\epsilon_r=10$, $E_g=1.500$ eV", ha="center", fontsize=11)
    fig.text(0.5, 0.15, f"Reduced mass = 0.080 m₀   |   Radius scale = {radius_nm:.3f} nm\n"
             "Optical positions (eV): " + ",  ".join(f"{v:.6f}" for v in optical_eV),
             ha="center", fontsize=11)
    fig.text(0.5, 0.045, "Positions only: equal stick heights do not predict strengths or widths.\n"
             "Permitted transitions and negligible recoil assumed; not an empirical spectrum.",
             ha="center", fontsize=10)
    fig.savefig(Path(__file__).stem + ".png", dpi=120, facecolor="white",
                transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()