codex/tunnel-diode-band-state-overlap.py

tunnel-diode-band-state-overlap.py
"""Original band/state diagram. Python 3.14; NumPy 2.3.5; Matplotlib 3.10.7.

Run from _media. Energies are deliberately arbitrary; no device parameters fit.
The n reservoir is left, p reservoir right; positive V lowers p electron energies.
"""
from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Patch

BLUE = '#2468a0'
ORANGE = '#cc7420'
GREEN = '#168253'
GAP = 2.2
ECN = -0.5
EVP0 = 0.5


def transfer_window(v):
    """Zero-T same-energy occupied-to-empty intervals, arbitrary energy units."""
    if v >= 0:
        return max(ECN, -v), min(0.0, EVP0 - v), 'n to p'
    return max(0.0, -GAP), min(-v, EVP0 - v), 'p to n'


def main():
    plt.rcParams.update({'font.size': 10, 'axes.titlesize': 11,
                         'figure.facecolor': 'white', 'axes.facecolor': 'white'})
    fig, axes = plt.subplots(2, 3, figsize=(12, 7.2), sharey=True)
    choices = [(0.0, 'Equilibrium'), (0.25, 'Forward: window opens'),
               (0.70, 'Forward: window shrinks'), (1.15, 'Forward: no band overlap'),
               (-0.70, 'Reverse: p to n transfer')]
    for ax, (v, title) in zip(axes.flat, choices):
        evp = EVP0 - v
        ecp = evp + GAP
        mup = -v
        # Only neutral reservoir states are shaded. The barrier has no added states.
        ax.fill_between([0, .30], -3.0, ECN-GAP, color=BLUE, alpha=.8)
        ax.fill_between([0, .30], 0, 3.5, facecolor='white',
                        edgecolor=ORANGE, hatch='///', linewidth=0)
        ax.fill_between([0, .30], ECN, 0, color=BLUE, alpha=.8)
        ax.fill_between([.70, 1], mup, evp, facecolor='white',
                        edgecolor=ORANGE, hatch='///', linewidth=0)
        ax.fill_between([.70, 1], -3.0, mup, color=BLUE, alpha=.8)
        ax.fill_between([.70, 1], ecp, 3.5, facecolor='white',
                        edgecolor=ORANGE, hatch='///', linewidth=0)
        x = np.linspace(0, 1, 301)
        u = np.clip((x-.30)/.40, 0, 1)
        bend = u*u*(3-2*u)
        ec = ECN + (ecp-ECN)*bend
        ax.plot(x, ec, color='#303940', lw=1.6)
        ax.plot(x, ec-GAP, color='#303940', lw=1.6)
        ax.plot([0, .32], [0, 0], '--', color=BLUE, lw=1.4)
        ax.plot([.68, 1], [mup, mup], '--', color=BLUE, lw=1.4)
        ax.text(.015, .11, r'$\mu_n$', color=BLUE, fontsize=10)
        ax.text(.88, mup+.10, r'$\mu_p$', color=BLUE, fontsize=10)
        ax.text(.03, ECN-.25, r'$E_{c,n}$', fontsize=9)
        ax.text(.73, evp+.13, r'$E_{v,p}$', fontsize=9)
        lo, hi, direction = transfer_window(v)
        if hi > lo:
            ax.plot([.50, .50], [lo, hi], color=GREEN, lw=5, solid_capstyle='butt')
            e = (lo+hi)/2
            ends = ((.24, e), (.76, e)) if v > 0 else ((.76, e), (.24, e))
            ax.annotate('', xy=ends[1], xytext=ends[0],
                        arrowprops={'arrowstyle': '->', 'color': GREEN, 'lw': 2})
            note = f'transfer window = {hi-lo:.2f}'
        else:
            note = 'net current = 0' if v == 0 else 'direct band transfer closed'
        ax.text(.5, -3.43, note, ha='center', color=GREEN, fontsize=9)
        ax.set_title(f'{title}\n$qV={v:+.2f}$ (chosen units)', pad=9)
        ax.set_xlim(-.04, 1.04)
        ax.set_ylim(-3.6, 3.65)
        ax.set_xticks([.15, .50, .85], [r'$n^+$', 'thin barrier', r'$p^+$'])
        ax.tick_params(axis='x', length=0)
        ax.set_yticks([-2, 0, 2])
        ax.spines[['top', 'right', 'bottom']].set_visible(False)
        ax.set_ylabel('Electron energy (arbitrary units)')
    key = axes.flat[-1]
    key.axis('off')
    key.legend(handles=[Patch(facecolor=BLUE, alpha=.8, label='occupied reservoir states'),
                        Patch(facecolor='white', edgecolor=ORANGE, hatch='///',
                              label='empty reservoir states'),
                        Patch(facecolor=GREEN, label='same-energy transfer window')],
               loc='upper left', frameon=False, fontsize=10)
    key.text(.03, .60, 'Each arrow conserves electron energy.\n'
             'Available final states are essential.\n\n'
             r'Positive bias: $\mu_n-\mu_p=qV$.' '\n'
             'At zero bias the occupations balance.\n\n'
             'Reservoir occupations use $T=0$.\n'
             'Band bending and barrier width are\n'
             'schematic; momentum selection and\n'
             'phonon assistance are omitted.', transform=key.transAxes,
             va='top', linespacing=1.45)
    fig.suptitle('Tunnel diode: occupied and empty states must overlap in energy', fontsize=15, y=.99)
    fig.text(.5, .012, 'Original qualitative construction; not a measured band profile or a fit to an Esaki device.',
             ha='center', fontsize=10)
    fig.tight_layout(rect=(0, .035, 1, .94), h_pad=2.0, w_pad=2.2)
    out = Path.cwd() / (Path(__file__).stem + '.png')
    fig.savefig(out, dpi=120, facecolor='white')
    plt.close(fig)


if __name__ == '__main__':
    main()