codexreactance-frequency-cancellation.py
reactance-frequency-cancellation.py
"""Toy series RLC predictions, never measured data.
Run from _media: MPLCONFIGDIR=/tmp/codex-wiki-matplotlib python ../reactance-frequency-cancellation.py
Writes reactance-frequency-cancellation.png to CWD.
Tested with Python 3.14.4, NumPy 2.3.5, Matplotlib 3.10.7.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
L, C, R, Rs, Vrms = 10e-3, 1e-6, 100.0, 10.0, 1.0
f0 = 1 / (2 * np.pi * np.sqrt(L * C))
f = np.unique(np.r_[np.geomspace(200, 8000, 900), f0])
w = 2 * np.pi * f
xl, xc = w * L, -1 / (w * C)
x = xl + xc
current = Vrms / np.hypot(R + Rs, x)
phase = np.degrees(np.arctan2(x, R + Rs))
plt.rcParams.update({"font.size": 11, "axes.spines.top": False,
"axes.spines.right": False, "figure.facecolor": "white",
"axes.facecolor": "white"})
fig = plt.figure(figsize=(10.4, 7.2), layout="constrained")
gs = fig.add_gridspec(2, 2, height_ratios=[1.15, 1])
top, left, right = fig.add_subplot(gs[0, :]), fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1])
fig.suptitle("Opposite reactances select a frequency\n"
"Toy prediction: L = 10 mH, C = 1 μF, RΣ = 100 + 10 = 110 Ω, Vᵣₘₛ = 1.00 V\n"
"Ideal calculated curves • no measured data", fontsize=13)
top.semilogx(f, xl, color="#b7560b", label=r"$X_L=2\pi fL$", linewidth=2)
top.semilogx(f, xc, color="#2855a5", label=r"$X_C=-1/(2\pi fC)$", linewidth=2)
top.semilogx(f, x, color="#177352", label=r"$X=X_L+X_C$", linewidth=2.5)
top.axhline(0, color="0.4", linewidth=0.8)
top.plot(f0, 0, "o", color="#177352")
top.annotate("Cancellation\nf₀ = 1.592 kHz", xy=(f0, 0), xytext=(2300, -470),
arrowprops={"arrowstyle": "->", "color": "0.3"}, fontsize=11)
top.set(ylabel="Signed reactance (Ω)", ylim=(-820, 570))
top.legend(loc="upper left", ncols=3, fontsize=11)
left.semilogx(f, 1000 * current, color="#177352", linewidth=2.3)
left.plot(f0, 1000 * Vrms / (R + Rs), "o", color="#177352")
left.annotate("9.091 mA", xy=(f0, 1000 * Vrms / (R + Rs)), xytext=(2500, 9.6),
arrowprops={"arrowstyle": "->", "color": "0.3"})
left.set(ylabel="RMS current (mA)", ylim=(0, 11), xlabel="Frequency f (Hz)")
right.semilogx(f, phase, color="#653c96", linewidth=2.3)
right.axhline(0, color="0.4", linewidth=0.8)
right.text(260, -43, "Current leads\nX < 0", color="#2855a5")
right.text(2700, 21, "Current lags\nX > 0", color="#b7560b")
right.set(ylabel="Voltage − current phase (degrees)", ylim=(-90, 90),
yticks=[-90, -45, 0, 45, 90], xlabel="Frequency f (Hz)")
for ax in (top, left, right):
ax.axvline(f0, color="0.45", linestyle=":", linewidth=1.2)
ax.set_xlim(200, 8000)
ax.set_xticks([200, 500, 1000, 2000, 5000, 8000])
ax.set_xticklabels(["200", "500", "1000", "2000", "5000", "8000"])
ax.grid(alpha=0.17)
fig.savefig(Path.cwd() / "reactance-frequency-cancellation.png", dpi=105,
facecolor="white", transparent=False)
plt.close(fig)
if __name__ == "__main__":
main()