codex/kirtley-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()