From 7d77e83d253219647539c98efa1d5006b87c418b Mon Sep 17 00:00:00 2001 From: Elzbieta Kotulska Date: Thu, 15 Aug 2024 09:20:06 +0200 Subject: [PATCH] Test pv_power formula with fallback components Signed-off-by: Elzbieta Kotulska --- tests/timeseries/test_logical_meter.py | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/timeseries/test_logical_meter.py b/tests/timeseries/test_logical_meter.py index 65ea41c9e..b5fb989e5 100644 --- a/tests/timeseries/test_logical_meter.py +++ b/tests/timeseries/test_logical_meter.py @@ -85,3 +85,69 @@ async def test_pv_power_no_pv_components(self, mocker: MockerFixture) -> None: await mockgrid.mock_resampler.send_non_existing_component_value() assert (await pv_power_receiver.receive()).value == Power.zero() + + async def test_pv_power_with_failing_meter(self, mocker: MockerFixture) -> None: + """Test the pv power formula.""" + mockgrid = MockMicrogrid(grid_meter=False, mocker=mocker) + mockgrid.add_solar_inverters(2) + + async with mockgrid, AsyncExitStack() as stack: + pv_pool = microgrid.new_pv_pool(priority=5) + stack.push_async_callback(pv_pool.stop) + pv_power_receiver = pv_pool.power.new_receiver() + # Wait for mock resampler to create forward stream + await asyncio.sleep(0.01) + + # Note: PvPowerFormula has a "nones-are-zero" rule, that says: + # * if the meter value is None, it should be treated as None. + # * for other components None is treated as 0. + + expected_input_output: list[ + tuple[list[float | None], list[float | None], Power | None] + ] = [ + # ([meter_power], [pv_inverter_power], expected_power) + # + # Case 1: Both meters are available, so inverters are not used. + ([-1.0, -2.0], [None, -5.0], Power.from_watts(-3.0)), + ([-1.0, -2.0], [-10.0, -20.0], Power.from_watts(-3.0)), + # Case 2: The first meter is unavailable (None). + # Subscribe to the fallback inverter, but return None as the result, + # according to the "nones-are-zero" rule + ([None, -2.0], [-10.0, -20.0], None), + # Case 3: First meter is unavailable (None). Fallback inverter provides + # a value. + ([None, -2.0], [-10.0, -20.0], Power.from_watts(-12.0)), + ([None, -2.0], [-11.0, -20.0], Power.from_watts(-13.0)), + # Case 4: Both first meter and its fallback inverter are unavailable + # (None). Return 0 according to the "nones-are-zero" rule. + ([None, -2.0], [None, -20.0], Power.from_watts(-2.0)), + ([None, -2.0], [-11.0, -20.0], Power.from_watts(-13.0)), + # Case 5: Both meters are unavailable (None). + # Subscribe to the fallback inverter, but return None as the result, + # according "nones-are-zero" rule + ([None, None], [-5.0, -20.0], None), + # Case 6: Both meters are unavailable (None). Fallback inverter provides + # a values. + ([None, None], [-5.0, -20.0], Power.from_watts(-25.0)), + # Case 7: All components are unavailable (None). + # Return 0 according to the "nones-are-zero" rule. + ([None, None], [None, None], Power.from_watts(0.0)), + ([None, None], [-5.0, -20.0], Power.from_watts(-25.0)), + # Case 8: Meters becomes available and inverter values are not used. + ([-10.0, None], [-5.0, -20.0], Power.from_watts(-30.0)), + ([-10.0, -2.0], [-5.0, -20.0], Power.from_watts(-12.0)), + ] + + for idx, (meter_power, pv_inverter_power, expected_power) in enumerate( + expected_input_output + ): + await mockgrid.mock_resampler.send_meter_power(meter_power) + await mockgrid.mock_resampler.send_pv_inverter_power(pv_inverter_power) + result = await pv_power_receiver.receive() + assert result.value == expected_power, ( + f"Test case {idx} failed:" + + f" meter_power: {meter_power}" + + f" pv_inverter_power {pv_inverter_power}" + + f" expected_power: {expected_power}" + + f" actual_power: {result.value}" + )