Skip to content

Commit 44aa6e2

Browse files
Merge pull request #7 from flora-hofmann-frequenz/add_energy_metric
Add helper function to calculate energy metric
2 parents de6439d + 910159f commit 44aa6e2

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

RELEASE_NOTES.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
## New Features
1212

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
13+
* Add cumulative_energy calculation to read out energy consumption and production over a specified time range.
1414

1515
## Bug Fixes
1616

pyproject.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ classifiers = [
2626
]
2727
requires-python = ">= 3.11, < 4"
2828
dependencies = [
29-
"typing-extensions >= 4.5.0, < 5",
29+
"typing-extensions >= 4.6.1, < 5",
30+
"frequenz-client-reporting>=0.8.0, < 1",
31+
"frequenz-client-common >= 0.2.0, < 0.3",
3032
]
3133
dynamic = ["version"]
3234

src/frequenz/reporting/_reporting.py

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# License: MIT
2+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3+
4+
"""A highlevel interface for the reporting API."""
5+
6+
from collections import namedtuple
7+
from datetime import datetime
8+
9+
from frequenz.client.common.metric import Metric
10+
from frequenz.client.reporting import ReportingApiClient
11+
12+
CumulativeEnergy = namedtuple(
13+
"CumulativeEnergy", ["start_time", "end_time", "consumption", "production"]
14+
)
15+
"""Type for cumulative energy consumption and production over a specified time."""
16+
17+
18+
# pylint: disable-next=too-many-arguments
19+
async def cumulative_energy(
20+
client: ReportingApiClient,
21+
microgrid_id: int,
22+
component_id: int,
23+
start_time: datetime,
24+
end_time: datetime,
25+
use_active_power: bool,
26+
resolution: int | None = None,
27+
) -> CumulativeEnergy:
28+
"""
29+
Calculate the cumulative energy consumption and production over a specified time range.
30+
31+
Args:
32+
client: The client used to fetch the metric samples from the Reporting API.
33+
microgrid_id: The ID of the microgrid.
34+
component_id: The ID of the component within the microgrid.
35+
start_time: The start date and time for the period.
36+
end_time: The end date and time for the period.
37+
use_active_power: If True, use the 'AC_ACTIVE_POWER' metric.
38+
If False, use the 'AC_ACTIVE_ENERGY' metric.
39+
resolution: The resampling resolution for the data, represented in seconds.
40+
If None, no resampling is applied.
41+
Returns:
42+
EnergyMetric: A named tuple with start_time, end_time, consumption, and production
43+
in Wh. Consumption has a positive sign, production has a negative sign.
44+
"""
45+
metric = Metric.AC_ACTIVE_POWER if use_active_power else Metric.AC_ACTIVE_ENERGY
46+
47+
metric_samples = [
48+
sample
49+
async for sample in client.list_microgrid_components_data(
50+
microgrid_components=[(microgrid_id, [component_id])],
51+
metrics=metric,
52+
start_dt=start_time,
53+
end_dt=end_time,
54+
resolution=resolution,
55+
)
56+
]
57+
58+
if metric_samples:
59+
if use_active_power:
60+
# Convert power to energy if using AC_ACTIVE_POWER
61+
consumption = (
62+
sum(
63+
m1.value * (m2.timestamp - m1.timestamp).total_seconds()
64+
for m1, m2 in zip(metric_samples, metric_samples[1:])
65+
if m1.value > 0
66+
)
67+
/ 3600.0
68+
) # Convert seconds to hours
69+
70+
last_value_consumption = (
71+
metric_samples[-1].value
72+
* (end_time - metric_samples[-1].timestamp).total_seconds()
73+
if metric_samples[-1].value > 0
74+
else 0
75+
) / 3600.0
76+
77+
consumption += last_value_consumption
78+
79+
production = (
80+
sum(
81+
m1.value * (m2.timestamp - m1.timestamp).total_seconds()
82+
for m1, m2 in zip(metric_samples, metric_samples[1:])
83+
if m1.value < 0
84+
)
85+
/ 3600.0
86+
)
87+
88+
last_value_production = (
89+
metric_samples[-1].value
90+
* (end_time - metric_samples[-1].timestamp).total_seconds()
91+
if metric_samples[-1].value < 0
92+
else 0
93+
) / 3600.0
94+
95+
production += last_value_production
96+
97+
else:
98+
# Directly use energy values if using AC_ACTIVE_ENERGY
99+
consumption = sum(
100+
m2.value - m1.value
101+
for m1, m2 in zip(metric_samples, metric_samples[1:])
102+
if m2.value - m1.value > 0
103+
)
104+
production = sum(
105+
m2.value - m1.value
106+
for m1, m2 in zip(metric_samples, metric_samples[1:])
107+
if m2.value - m1.value < 0
108+
)
109+
110+
if len(metric_samples) > 1:
111+
last_value_diff = metric_samples[-1].value - metric_samples[-2].value
112+
if last_value_diff > 0:
113+
consumption += last_value_diff
114+
elif last_value_diff < 0:
115+
production += last_value_diff
116+
else:
117+
consumption = production = 0.0
118+
119+
return CumulativeEnergy(
120+
start_time=start_time,
121+
end_time=end_time,
122+
consumption=consumption,
123+
production=production,
124+
)

0 commit comments

Comments
 (0)