codexdiode-characteristic-model-and-ideal.py
diode-characteristic-model-and-ideal.py
"""Draw original diode model comparisons; Python 3.14, NumPy 2.3.5, Matplotlib 3.10.7.
Run from _media: this script writes its matching PNG basename to the CWD.
"""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
k_b, q, temperature = 1.380649e-23, 1.602176634e-19, 300.0
saturation, ideality, series = 1e-9, 2.0, 20.0
thermal = k_b * temperature / q
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white",
"axes.facecolor": "white"})
fig, (model, ideal) = plt.subplots(1, 2, figsize=(10, 5.8), dpi=120,
gridspec_kw={"width_ratios": [1.5, 1]})
fig.subplots_adjust(left=0.075, right=0.975, bottom=0.16, top=0.78, wspace=0.32)
fig.suptitle("Diode characteristics: smooth models and an ideal switch", y=0.96, fontsize=15)
fig.text(0.5, 0.875, r"Constructed curves: $I_S=1$ nA, $n=2$, $T=300$ K; $V_T=25.8520$ mV",
ha="center", fontsize=11)
current = np.linspace(0, 0.014, 2500)
junction = ideality * thermal * np.log1p(current / saturation)
negative_v = np.linspace(-0.3, 0, 300)
negative_i = saturation * np.expm1(negative_v / (ideality * thermal))
model.plot(np.r_[negative_v, junction], np.r_[negative_i, current] * 1e3,
color="#1565a6", lw=2.3, label="Shockley junction model")
model.plot(junction + current * series, current * 1e3,
color="#a94400", lw=2.3, label=r"With series $R_s=20\,\Omega$")
for voltage, offset in [(0.6, (-68, 21)), (0.8, (-125, -40))]:
value = saturation * np.expm1(voltage / (ideality * thermal)) * 1e3
model.scatter([voltage], [value], color="#1565a6", s=28, zorder=5)
model.annotate(f"{voltage:.1f} V: {value:.4f} mA", (voltage, value),
xytext=offset, textcoords="offset points", fontsize=10,
arrowprops={"arrowstyle": "-", "color": "#1565a6"})
model.set(xlim=(-0.32, 1.18), ylim=(-0.4, 14.5),
xlabel=r"Terminal voltage $V_D=\phi_A-\phi_K$ (V)",
ylabel=r"Current $I_D$, positive A to K (mA)", title="Finite forward voltage")
model.axhline(0, color="0.55", lw=0.7)
model.grid(alpha=0.18)
model.legend(loc="upper left", fontsize=10, frameon=False)
inset = model.inset_axes([0.12, 0.50, 0.36, 0.25])
inset.plot(negative_v, negative_i * 1e9, color="#1565a6", lw=2)
inset.axhline(-1, color="0.55", lw=1, ls=":")
inset.set(xlim=(-0.3, 0), ylim=(-1.12, 0.1), title="Reverse branch (nA)")
inset.set_xticks([-0.3, 0], labels=["−0.3 V", "0 V"])
inset.set_yticks([-1, 0])
inset.tick_params(labelsize=9)
inset.title.set_fontsize(10)
ideal.plot([-0.32, 0], [0, 0], color="#6e3c9c", lw=3)
ideal.plot([0, 0], [0, 14], color="#6e3c9c", lw=3)
ideal.scatter([0], [0], color="#6e3c9c", s=35, zorder=5)
ideal.annotate("Conducting\n" + r"$V_D=0,\ I_D\geq0$", (0, 9),
xytext=(0.28, 9), arrowprops={"arrowstyle": "->"}, fontsize=11)
ideal.annotate("Blocking\n" + r"$I_D=0,\ V_D\leq0$", (-0.22, 0),
xytext=(-0.28, 3.3), arrowprops={"arrowstyle": "->"}, fontsize=11)
ideal.set(xlim=(-0.35, 0.85), ylim=(-0.4, 14.5), xlabel=r"Terminal voltage $V_D$ (V)",
ylabel=r"Current $I_D$ (mA)", title="Ideal diode: allowed states")
ideal.axhline(0, color="0.55", lw=0.7)
ideal.grid(alpha=0.18)
fig.text(0.5, 0.027, "The ideal switch has zero forward drop and no reverse leakage; neither model includes breakdown.",
ha="center", fontsize=10)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()