Skip to content
Merged
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
27 changes: 16 additions & 11 deletions src/frequenz/sdk/timeseries/battery_pool/_metric_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@


import logging
import math
from abc import ABC, abstractmethod
from collections.abc import Mapping, Set
from datetime import datetime, timezone
from typing import Generic, TypeVar

from ... import timeseries
from ..._internal import _math
from ...actor.power_distributing._component_managers._battery_manager import (
_get_battery_inverter_mappings,
)
Expand Down Expand Up @@ -361,9 +363,9 @@ def calculate(
Return None if there are no component metrics.
"""
timestamp = _MIN_TIMESTAMP
usable_capacity_x100: float = 0
used_capacity_x100: float = 0
total_capacity_x100: float = 0
usable_capacity_x100: float = 0.0
used_capacity_x100: float = 0.0
total_capacity_x100: float = 0.0

for battery_id in working_batteries:
if battery_id not in metrics_data:
Expand Down Expand Up @@ -395,26 +397,29 @@ def calculate(
# Therefore, the variables are named with a `_x100` suffix.
usable_capacity_x100 = capacity * (soc_upper_bound - soc_lower_bound)
soc_scaled = (
(soc - soc_lower_bound) / (soc_upper_bound - soc_lower_bound) * 100
(soc - soc_lower_bound) / (soc_upper_bound - soc_lower_bound) * 100.0
)
# we are clamping here because the SoC might be out of bounds
soc_scaled = min(max(soc_scaled, 0), 100)
soc_scaled = min(max(soc_scaled, 0.0), 100.0)
timestamp = max(timestamp, metrics.timestamp)
used_capacity_x100 += usable_capacity_x100 * soc_scaled
total_capacity_x100 += usable_capacity_x100

if timestamp == _MIN_TIMESTAMP:
return Sample(datetime.now(tz=timezone.utc), None)

# When the calculated is close to 0.0 or 100.0, they are set to exactly 0.0 or
# 100.0, to make full/empty checks using the == operator less error prone.
pct = 0.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a comment about the intention of pct being literally 0.0 and 100.0 on the edges, just in case anyone feels tempted to simplify the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# To avoid zero division error
if total_capacity_x100 == 0:
return Sample(
timestamp=timestamp,
value=Percentage.from_percent(0.0),
)
if not _math.is_close_to_zero(total_capacity_x100):
pct = used_capacity_x100 / total_capacity_x100
if math.isclose(pct, 100.0):
pct = 100.0

return Sample(
timestamp=timestamp,
value=Percentage.from_percent(used_capacity_x100 / total_capacity_x100),
value=Percentage.from_percent(pct),
)


Expand Down