codextunnel-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()