Skip to content

Commit c32d84c

Browse files
Add more formula tests for min and max functions (#623)
Also we make the interface public as it's considered ready to use.
2 parents 1e4c9ea + 53912ed commit c32d84c

File tree

4 files changed

+133
-10
lines changed

4 files changed

+133
-10
lines changed

RELEASE_NOTES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@
2727
- Provide access to `capacity` (maximum number of elements) in `MovingWindow`.
2828
- Methods to retrieve oldest and newest timestamp of valid samples are added to both.
2929

30-
3130
- Now when printing `FormulaEngine` for debugging purposes the the formula will be shown in infix notation, which should be easier to read.
3231

3332
- The CI now runs cross-arch tests on `arm64` architectures.
3433

34+
- The `min` and `max` functions in the `FormulaEngine` are now public. Note that the same functions have been public in the builder.
35+
3536
## Bug Fixes
3637

3738
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->

src/frequenz/sdk/timeseries/_formula_engine/_formula_engine.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def __truediv__(
165165
"""
166166
return self._higher_order_builder(self, self._create_method) / other # type: ignore
167167

168-
def _max(
168+
def max(
169169
self, other: _GenericEngine | _GenericHigherOrderBuilder | QuantityT
170170
) -> _GenericHigherOrderBuilder:
171171
"""Return a formula engine that outputs the maximum of `self` and `other`.
@@ -180,7 +180,7 @@ def _max(
180180
"""
181181
return self._higher_order_builder(self, self._create_method).max(other) # type: ignore
182182

183-
def _min(
183+
def min(
184184
self, other: _GenericEngine | _GenericHigherOrderBuilder | QuantityT
185185
) -> _GenericHigherOrderBuilder:
186186
"""Return a formula engine that outputs the minimum of `self` and `other`.

tests/timeseries/_formula_engine/test_formula_composition.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,68 @@ async def test_formula_composition_missing_bat(self, mocker: MockerFixture) -> N
171171
assert count == 10
172172

173173
async def test_formula_composition_min_max(self, mocker: MockerFixture) -> None:
174-
"""Test the composition of formulas with min/max values."""
174+
"""Test the composition of formulas with the min and max."""
175175
mockgrid = MockMicrogrid(grid_meter=True)
176+
mockgrid.add_chps(1)
176177
await mockgrid.start(mocker)
177178

178179
logical_meter = microgrid.logical_meter()
179-
engine_min = logical_meter.grid_power._min( # pylint: disable=protected-access
180-
Power.zero()
181-
).build("grid_power_min")
180+
engine_min = logical_meter.grid_power.min(logical_meter.chp_power).build(
181+
"grid_power_min"
182+
)
183+
engine_min_rx = engine_min.new_receiver()
184+
engine_max = logical_meter.grid_power.max(logical_meter.chp_power).build(
185+
"grid_power_max"
186+
)
187+
engine_max_rx = engine_max.new_receiver()
188+
189+
await mockgrid.mock_resampler.send_meter_power([100.0, 200.0])
190+
191+
# Test min
192+
min_pow = await engine_min_rx.receive()
193+
assert (
194+
min_pow and min_pow.value and min_pow.value.isclose(Power.from_watts(100.0))
195+
)
196+
197+
# Test max
198+
max_pow = await engine_max_rx.receive()
199+
assert (
200+
max_pow and max_pow.value and max_pow.value.isclose(Power.from_watts(200.0))
201+
)
202+
203+
await mockgrid.mock_resampler.send_meter_power([-100.0, -200.0])
204+
205+
# Test min
206+
min_pow = await engine_min_rx.receive()
207+
assert (
208+
min_pow
209+
and min_pow.value
210+
and min_pow.value.isclose(Power.from_watts(-200.0))
211+
)
212+
213+
# Test max
214+
max_pow = await engine_max_rx.receive()
215+
assert (
216+
max_pow
217+
and max_pow.value
218+
and max_pow.value.isclose(Power.from_watts(-100.0))
219+
)
220+
221+
await engine_min._stop() # pylint: disable=protected-access
222+
await mockgrid.cleanup()
223+
await logical_meter.stop()
224+
225+
async def test_formula_composition_min_max_const(
226+
self, mocker: MockerFixture
227+
) -> None:
228+
"""Test the compositing formulas and constants with the min and max functions."""
229+
mockgrid = MockMicrogrid(grid_meter=True)
230+
await mockgrid.start(mocker)
231+
232+
logical_meter = microgrid.logical_meter()
233+
engine_min = logical_meter.grid_power.min(Power.zero()).build("grid_power_min")
182234
engine_min_rx = engine_min.new_receiver()
183-
engine_max = logical_meter.grid_power._max( # pylint: disable=protected-access
184-
Power.zero()
185-
).build("grid_power_max")
235+
engine_max = logical_meter.grid_power.max(Power.zero()).build("grid_power_max")
186236
engine_max_rx = engine_max.new_receiver()
187237

188238
await mockgrid.mock_resampler.send_meter_power([100.0])

tests/timeseries/test_formula_engine.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,78 @@ async def test_simple(self) -> None:
439439
],
440440
)
441441

442+
async def test_min_max(self) -> None:
443+
"""Test min and max functions in combination."""
444+
await self.run_test(
445+
3,
446+
lambda c2, c4, c5: c2.min(c4).max(c5),
447+
[
448+
([4.0, 6.0, 5.0], 5.0),
449+
],
450+
)
451+
452+
async def test_max(self) -> None:
453+
"""Test the max function."""
454+
await self.run_test(
455+
3,
456+
lambda c2, c4, c5: c2 * c4.max(c5),
457+
[
458+
([10.0, 12.0, 15.0], 150.0),
459+
],
460+
)
461+
await self.run_test(
462+
3,
463+
lambda c2, c4, c5: (c2 * c4).max(c5),
464+
[
465+
([10.0, 12.0, 15.0], 120.0),
466+
],
467+
)
468+
await self.run_test(
469+
3,
470+
lambda c2, c4, c5: (c2 + c4).max(c5),
471+
[
472+
([10.0, 12.0, 15.0], 22.0),
473+
],
474+
)
475+
await self.run_test(
476+
3,
477+
lambda c2, c4, c5: c2 + c4.max(c5),
478+
[
479+
([10.0, 12.0, 15.0], 25.0),
480+
],
481+
)
482+
483+
async def test_min(self) -> None:
484+
"""Test the min function."""
485+
await self.run_test(
486+
3,
487+
lambda c2, c4, c5: (c2 * c4).min(c5),
488+
[
489+
([10.0, 12.0, 15.0], 15.0),
490+
],
491+
)
492+
await self.run_test(
493+
3,
494+
lambda c2, c4, c5: c2 * c4.min(c5),
495+
[
496+
([10.0, 12.0, 15.0], 120.0),
497+
],
498+
)
499+
await self.run_test(
500+
3,
501+
lambda c2, c4, c5: (c2 + c4).min(c5),
502+
[
503+
([10.0, 2.0, 15.0], 12.0),
504+
],
505+
)
506+
await self.run_test(
507+
3,
508+
lambda c2, c4, c5: c2 + c4.min(c5),
509+
[
510+
([10.0, 12.0, 15.0], 22.0),
511+
],
512+
)
513+
442514
async def test_compound(self) -> None:
443515
"""Test compound formulas."""
444516
await self.run_test(

0 commit comments

Comments
 (0)