codex/reactive-power-overcompensation.py

reactive-power-overcompensation.py
"""Original fixed-load model: leading current need not exceed the baseline."""
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def main():
    power, reactive, voltage, frequency = 1000.0, 750.0, 200.0, 50.0
    omega = 2 * np.pi * frequency
    optimum = reactive / (omega * voltage**2)
    capacitance = np.linspace(0, 180e-6, 1201)
    net_q = reactive - omega * capacitance * voltage**2
    current = np.hypot(power, net_q) / voltage
    baseline = np.hypot(power, reactive) / voltage
    assert np.isclose(optimum * 1e6, 59.683103659460755)
    assert baseline == 6.25
    assert np.isclose(np.hypot(power, reactive - omega * 2 * optimum * voltage**2)
                      / voltage, baseline)
    assert np.isclose(np.hypot(power, reactive - omega * 100e-6 * voltage**2)
                      / voltage, 5.6050894549555)
    assert np.allclose(current**2 - baseline**2,
                       (omega * capacitance * voltage**2)
                       * (omega * capacitance * voltage**2 - 2 * reactive) / voltage**2)

    fig, (q_axes, i_axes) = plt.subplots(1, 2, figsize=(10, 4.2), dpi=100,
                                       layout="constrained")
    fig.set_facecolor("white")
    q_axes.plot(capacitance * 1e6, net_q, color="#286f99")
    q_axes.axhline(0, color="#555555", linewidth=0.8)
    q_axes.set(ylabel="Net reactive power (var)", title="Capacitance changes the sign of Q")
    q_axes.text(5, 430, "Lagging", color="#286f99")
    q_axes.text(100, -1400, "Leading", color="#9f4b27")
    i_axes.plot(capacitance * 1e6, current, color="#9f4b27")
    i_axes.axhline(baseline, color="#555555", linestyle="--", label="Uncorrected: 6.25 A")
    i_axes.axvspan(optimum * 1e6, 2 * optimum * 1e6, color="#7d903b", alpha=0.13,
                   label="Leading, but below baseline current")
    i_axes.scatter([optimum * 1e6], [power / voltage], color="#286f99", zorder=4)
    i_axes.set(ylabel="RMS source current (A)", ylim=(4.5, 10.2),
               title="Minimum at cancellation; baseline at 2C*")
    i_axes.legend(loc="upper left", fontsize=8)
    for ax in (q_axes, i_axes):
        ax.axvline(optimum * 1e6, color="#286f99", linestyle=":", linewidth=1)
        ax.axvline(2 * optimum * 1e6, color="#7d903b", linestyle=":", linewidth=1)
        ax.set(xlabel="Shunt capacitance (µF)", xlim=(0, 180))
        ax.set_xticks([0, optimum * 1e6, 2 * optimum * 1e6, 180],
                      ["0", "C*\n59.7", "2C*\n119.4", "180"])
        ax.grid(alpha=0.15)
    fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white")
    plt.close(fig)


if __name__ == "__main__":
    main()