Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ This release updates the microgrid component graph library to v0.6.0, which stop
- Producer power used to clamp each producer at zero. A producer that draws power now adds a positive value instead of zero, so the total can be positive. For example, PV producing 10 kW and a CHP drawing 2 kW used to give -10 kW and now give -8 kW. There is one exception: when the two share a meter below the grid meter, that meter sends data, and `disable_fallback_components` is off, the meter measures them together, so they gave -8 kW before too. `producer.power.min(Power.zero()).build("producer_power")` clamps the total at zero, but it still differs from the old result when one producer draws power while another produces.
- With `ComponentGraphConfig(include_phantom_loads_in_consumer_formula=True)`, consumer power is unchanged: it still clamps each of its terms at zero.

## Bug Fixes

* Make the PowerManager fall back to zero instead of changing the requested power direction when a target is snapped outside asymmetric exclusion bounds.
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,14 @@ def _get_nearest_possible_power(
case (None, p) | (p, None) if p:
return p
case (low, high) if low and high:
if high - power < power - low:
return high
return low
# The exclusion bounds are a region where the system cannot operate.
nearest = high if high - power < power - low else low
# Never turn a request into power in the opposite direction.
if (power > Power.zero() and nearest < Power.zero()) or (
power < Power.zero() and nearest > Power.zero()
):
return Power.zero()
return nearest
case _:
return Power.zero()

Expand Down Expand Up @@ -197,9 +202,10 @@ def _calc_targets( # pylint: disable=too-many-branches,too-many-statements

if allocations:
_logger.info(
"PowerManager allocations for component IDs: %s: %s",
"PowerManager allocations for component IDs: %s: %s, target power: %s",
sorted(component_ids),
allocations,
target_power,
)

return target_power, Bounds[Power](lower=lower_bound, upper=upper_bound)
Expand Down
40 changes: 40 additions & 0 deletions tests/actor/_power_managing/test_shifting_matryoshka.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# pylint: disable=duplicate-code

import asyncio
import logging
import re
from datetime import datetime, timedelta, timezone

Expand Down Expand Up @@ -397,6 +398,45 @@ async def test_matryoshka_with_excl_3() -> None:
tester.bounds(priority=0, expected_power=-50.0, expected_bounds=(-60.0, 5.0))


@pytest.mark.parametrize(
("requested_power", "exclusion_bounds", "expected_power"),
[
(-3.0, (-35.0, 7.0), 0.0),
(3.0, (-7.0, 35.0), 0.0),
(-30.0, (-35.0, 7.0), -35.0),
],
)
async def test_matryoshka_avoids_opposite_direction_outside_exclusion_bounds(
caplog: pytest.LogCaptureFixture,
requested_power: float,
exclusion_bounds: tuple[float, float],
expected_power: float,
) -> None:
"""Avoid targets that would snap to the opposite direction."""
batteries = frozenset({ComponentId(2), ComponentId(5)})
system_bounds = _base_types.SystemBounds(
timestamp=datetime.now(tz=timezone.utc),
inclusion_bounds=timeseries.Bounds(
lower=Power.from_watts(-200.0), upper=Power.from_watts(200.0)
),
exclusion_bounds=timeseries.Bounds(
lower=Power.from_watts(exclusion_bounds[0]),
upper=Power.from_watts(exclusion_bounds[1]),
),
)
tester = StatefulTester(batteries, system_bounds)

with caplog.at_level(logging.INFO):
tester.tgt_power(
priority=1,
power=requested_power,
bounds=(-200.0, 200.0),
expected=expected_power,
)

assert f"target power: {expected_power:g} W" in caplog.messages[-1]


async def test_matryoshka_drop_old_proposals() -> None:
"""Tests for the power managing actor.

Expand Down