codexkirtley-ac-power-waveform.py
kirtley-ac-power-waveform.py
"""Original 20-V RMS, 3+4j-ohm load: phase, instantaneous and mean power."""
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
def main():
potential = 20.0
impedance = 3 + 4j
current = potential / impedance
complex_power = potential * np.conjugate(current)
assert np.isclose(current, 2.4 - 3.2j)
assert np.isclose(complex_power, 48 + 64j)
phase_lag = -np.angle(current)
x = np.linspace(0, 2 * np.pi, 2049)
v = np.sqrt(2) * potential * np.cos(x)
i = np.sqrt(2) * abs(current) * np.cos(x - phase_lag)
power = v * i
assert np.allclose(power, 48 + 80 * np.cos(2 * x - phase_lag))
assert np.isclose(np.trapezoid(power, x) / (2 * np.pi), 48)
fig, (signals, transfer) = plt.subplots(1, 2, figsize=(10, 4.2), dpi=100,
layout="constrained")
fig.set_facecolor("white")
signals.plot(x, v / (np.sqrt(2) * potential), color="#246b96", label="v / peak v")
signals.plot(x, i / (np.sqrt(2) * abs(current)), color="#b66d32", label="i / peak i")
signals.set(ylabel="Normalized waveform", ylim=(-1.2, 1.42),
title="Current lags by 53.13°; power factor = 0.6")
signals.legend(loc="upper right", fontsize=9)
transfer.plot(x, power, color="#246b96", label="Instantaneous power")
transfer.fill_between(x, power, 0, where=power < 0, interpolate=True,
color="#b66d32", alpha=0.35, label="Energy return")
transfer.axhline(48, color="#333333", linestyle="--", linewidth=1.2,
label="Cycle average = 48 W")
transfer.set(ylabel="Power (W)", ylim=(-45, 180),
title="Power ranges from −32 to 128 W")
transfer.legend(loc="upper right", fontsize=8.5)
for ax in [signals, transfer]:
ax.set(xlim=(0, 2 * np.pi), xlabel="Phase ωt (radians)",
xticks=np.arange(5) * np.pi / 2,
xticklabels=["0", "π/2", "π", "3π/2", "2π"])
ax.axhline(0, color="#777777", linewidth=0.6)
ax.grid(alpha=0.15)
fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()