"""Tests for power_analysis module: average power, efficiency, power factor.""" import numpy as np import pytest from mcltspice.power_analysis import ( compute_average_power, compute_efficiency, compute_instantaneous_power, compute_power_metrics, ) class TestComputeAveragePower: def test_dc_power(self): """DC: P = V * I exactly.""" n = 1000 t = np.linspace(0, 1.0, n) v = np.full(n, 5.0) i = np.full(n, 2.0) p = compute_average_power(t, v, i) assert p == pytest.approx(10.0, rel=1e-6) def test_ac_in_phase(self): """In-phase AC: P_avg = Vpk*Ipk/2.""" n = 10000 t = np.linspace(0, 0.01, n, endpoint=False) # 1 full period at 100 Hz freq = 100.0 Vpk = 10.0 Ipk = 2.0 v = Vpk * np.sin(2 * np.pi * freq * t) i = Ipk * np.sin(2 * np.pi * freq * t) p = compute_average_power(t, v, i) expected = Vpk * Ipk / 2.0 # = Vrms * Irms assert p == pytest.approx(expected, rel=0.02) def test_ac_quadrature(self): """90-degree phase shift: P_avg ~ 0 (reactive power only).""" n = 10000 t = np.linspace(0, 0.01, n, endpoint=False) freq = 100.0 v = np.sin(2 * np.pi * freq * t) i = np.cos(2 * np.pi * freq * t) # 90 deg shifted p = compute_average_power(t, v, i) assert p == pytest.approx(0.0, abs=0.01) def test_short_signal(self): assert compute_average_power(np.array([0.0]), np.array([5.0]), np.array([2.0])) == 0.0 class TestComputeEfficiency: def test_known_efficiency(self): """Input 10W, output 8W -> 80% efficiency.""" n = 1000 t = np.linspace(0, 1.0, n) vin = np.full(n, 10.0) iin = np.full(n, 1.0) # 10W input vout = np.full(n, 8.0) iout = np.full(n, 1.0) # 8W output result = compute_efficiency(t, vin, iin, vout, iout) assert result["efficiency_percent"] == pytest.approx(80.0, rel=0.01) assert result["input_power_watts"] == pytest.approx(10.0, rel=0.01) assert result["output_power_watts"] == pytest.approx(8.0, rel=0.01) assert result["power_dissipated_watts"] == pytest.approx(2.0, rel=0.01) def test_zero_input_power(self): """Zero input -> 0% efficiency (avoid division by zero).""" n = 100 t = np.linspace(0, 1.0, n) zeros = np.zeros(n) result = compute_efficiency(t, zeros, zeros, zeros, zeros) assert result["efficiency_percent"] == 0.0 class TestPowerFactor: def test_dc_power_factor(self): """DC signals (in phase) should have PF = 1.0.""" n = 1000 t = np.linspace(0, 1.0, n) v = np.full(n, 5.0) i = np.full(n, 2.0) result = compute_power_metrics(t, v, i) assert result["power_factor"] == pytest.approx(1.0, rel=0.01) def test_ac_in_phase_power_factor(self): """In-phase AC should have PF ~ 1.0.""" n = 10000 t = np.linspace(0, 0.01, n, endpoint=False) freq = 100.0 v = np.sin(2 * np.pi * freq * t) i = np.sin(2 * np.pi * freq * t) result = compute_power_metrics(t, v, i) assert result["power_factor"] == pytest.approx(1.0, rel=0.05) def test_ac_quadrature_power_factor(self): """90-degree phase shift -> PF ~ 0.""" n = 10000 t = np.linspace(0, 0.01, n, endpoint=False) freq = 100.0 v = np.sin(2 * np.pi * freq * t) i = np.cos(2 * np.pi * freq * t) result = compute_power_metrics(t, v, i) assert result["power_factor"] == pytest.approx(0.0, abs=0.05) def test_empty_signals(self): result = compute_power_metrics(np.array([]), np.array([]), np.array([])) assert result["power_factor"] == 0.0 class TestInstantaneousPower: def test_element_wise(self): v = np.array([1.0, 2.0, 3.0]) i = np.array([0.5, 1.0, 1.5]) p = compute_instantaneous_power(v, i) np.testing.assert_array_almost_equal(p, [0.5, 2.0, 4.5]) def test_complex_uses_real(self): """Should use real parts only.""" v = np.array([3.0 + 4j]) i = np.array([2.0 + 1j]) p = compute_instantaneous_power(v, i) assert p[0] == pytest.approx(6.0) # 3 * 2