domainiac 9.2.0__py3-none-any.whl → 9.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
domainiac/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .managers import (
2
+ AvailabilityManager,
2
3
  MeteringManager,
3
4
  NWPManager,
4
5
  PlantManager,
@@ -1,3 +1,4 @@
1
+ from .availability_manager import AvailabilityManager
1
2
  from .masterdata_manager import MasterdataManager
2
3
  from .metering_manager import MeteringManager
3
4
  from .nwp_manager import NWPManager
@@ -0,0 +1,58 @@
1
+ import datamazing.pandas as pdz
2
+ import pandas as pd
3
+
4
+ from .outage_manager import OutageManager
5
+
6
+
7
+ class AvailabilityManager:
8
+ """
9
+ Manager which simplifies the process of getting availability data.
10
+ """
11
+
12
+ def __init__(
13
+ self,
14
+ db: pdz.Database,
15
+ time_interval: pdz.TimeInterval,
16
+ resolution: pd.Timedelta,
17
+ ) -> None:
18
+ self.db = db
19
+ self.time_interval = time_interval
20
+ self.resolution = resolution
21
+ self.outage_manager = OutageManager(db, time_interval, resolution)
22
+
23
+ def get_availability(self) -> pd.DataFrame:
24
+ df_availability = self.db.query("scheduleAvailability", self.time_interval)
25
+ df_outage = self.outage_manager.get_plant_outage_time_series()
26
+
27
+ # Rename resource_gsrn to plant_gsrn for consistent merge
28
+ df_availability = df_availability.rename(
29
+ columns={
30
+ "resource_gsrn": "plant_gsrn",
31
+ "schedule_resource_capacity_max_MW": "capacity_max_MW",
32
+ "schedule_resource_capacity_min_MW": "capacity_min_MW",
33
+ }
34
+ )
35
+
36
+ required_columns = [
37
+ "time_utc",
38
+ "plant_gsrn",
39
+ "capacity_max_MW",
40
+ "capacity_min_MW",
41
+ ]
42
+
43
+ # Create an indicator column in outage to mark where outages exist
44
+ df_outage["has_outage"] = True
45
+
46
+ # Merge availability with outage data
47
+ df_available = df_availability.merge(
48
+ df_outage, on=["plant_gsrn", "time_utc"], how="outer"
49
+ )
50
+
51
+ # Where there's an outage, set capacity to zero
52
+ has_outage = df_available["has_outage"].fillna(False)
53
+ df_available.loc[has_outage, "capacity_max_MW"] = 0
54
+ df_available.loc[has_outage, "capacity_min_MW"] = 0
55
+
56
+ # Return only the required columns
57
+ df_available = df_available[required_columns]
58
+ return df_available
@@ -1,3 +1,5 @@
1
+ from functools import lru_cache
2
+
1
3
  import datamazing.pandas as pdz
2
4
  import numpy as np
3
5
  import pandas as pd
@@ -9,26 +11,36 @@ from ..modeling.nwp import Coordinate, Neighborhood, NWPParameter, NWPProvider
9
11
 
10
12
 
11
13
  class NWPManager:
12
- """
13
- Contains all logic concerning NWP data.
14
- This includes:
15
- - Getting NWP data
16
- - Getting average NWP data
17
- - Finding closest NWP coordinates
18
- """
19
-
20
14
  def __init__(
21
15
  self,
22
16
  db: pdz.Database,
23
17
  time_interval: pdz.TimeInterval,
24
18
  resolution: pd.Timedelta,
19
+ cache_nwp: bool = True,
25
20
  ):
21
+ """
22
+ Contains all logic concerning NWP data.
23
+ This includes:
24
+ - Getting NWP data
25
+ - Getting average NWP data
26
+ - Finding closest NWP coordinates
27
+
28
+ Args:
29
+ db (pdz.Database): Database backend
30
+ time_interval (pdz.TimeInterval): Time interval
31
+ resolution (pd.Timedelta): Time resolution
32
+ cache_nwp (bool, optional): Current implementation of this managers
33
+ is not very efficient when querying multiple coordinates. To mitigate
34
+ this, database queries can be cached. This is especially useful when
35
+ querying multiple coordinates. Defaults to True.
36
+ """
26
37
  self.db = db
27
38
  self.time_interval = time_interval
28
39
  self.resolution = resolution
29
40
 
30
41
  self._nwp_coordinates_kd_tree = dict()
31
42
  self._nwp_table_name_prefix = "forecastNwp"
43
+ self.cache_nwp = cache_nwp
32
44
 
33
45
  @staticmethod
34
46
  def calculate_wind_speed_from_vectors(u: pd.Series, v: pd.Series) -> pd.Series:
@@ -111,15 +123,24 @@ class NWPManager:
111
123
 
112
124
  return coordinates
113
125
 
126
+ @lru_cache()
127
+ def _query_cached(
128
+ self,
129
+ table: str,
130
+ time_interval: pdz.TimeInterval,
131
+ ) -> pd.DataFrame:
132
+ """
133
+ Query the database with caching.
134
+ """
135
+ df = self.db.query(table, time_interval)
136
+ return df
137
+
114
138
  def _get_nwp_parameter(
115
139
  self,
116
140
  provider: NWPProvider,
117
141
  parameter: NWPParameter,
118
142
  coordinate: Coordinate,
119
143
  ) -> pd.DataFrame:
120
- """
121
- Get NWP data for a given parameter, coordinate and altitude.
122
- """
123
144
  table = self._get_table_name(provider, parameter)
124
145
 
125
146
  filters = {
@@ -138,7 +159,13 @@ class NWPManager:
138
159
  self.time_interval.right + query_margin,
139
160
  )
140
161
 
141
- df = self.db.query(table, padded_time_interval, filters=filters)
162
+ # Cache query if specified
163
+ # This is useful when querying multiple coordinates.
164
+ if self.cache_nwp:
165
+ df = self._query_cached(table, padded_time_interval)
166
+ df = df.loc[(df[list(filters)] == pd.Series(filters)).all(axis=1)]
167
+ else:
168
+ df = self.db.query(table, padded_time_interval, filters=filters)
142
169
 
143
170
  df = df.drop(
144
171
  columns=[
@@ -160,10 +187,6 @@ class NWPManager:
160
187
  parameter: NWPParameter,
161
188
  coordinate: Coordinate,
162
189
  ):
163
- """
164
- Get NWP data for a given parameter, coordinate and altitude,
165
- using the proper interpolation technique.
166
- """
167
190
  df = self._get_nwp_parameter(provider, parameter, coordinate)
168
191
 
169
192
  df = df.sort_values(by="time_utc")
@@ -214,9 +237,12 @@ class NWPManager:
214
237
  parameter: NWPParameter,
215
238
  coordinate: Coordinate,
216
239
  ):
217
- df_nwp = self._get_interpolated_nwp_parameter(provider, parameter, coordinate)
240
+ df_nwp = self._get_interpolated_nwp_parameter(
241
+ provider,
242
+ parameter,
243
+ coordinate,
244
+ )
218
245
 
219
- # Ensure data is within time_interval defined by user
220
246
  df_nwp = df_nwp.query(
221
247
  f"time_utc >= '{self.time_interval.left}' "
222
248
  f"and time_utc <= '{self.time_interval.right}'",
@@ -230,14 +256,15 @@ class NWPManager:
230
256
  parameter: NWPParameter,
231
257
  neighborhood: Neighborhood,
232
258
  ) -> pd.DataFrame:
233
- """
234
- Get average NWP data for a given parameter, coordinates and altitude.
235
- """
236
259
  dfs = []
237
260
 
238
261
  neighbors = self.get_nwp_neighbors(provider, parameter, neighborhood)
239
262
  for coordinate in neighbors:
240
- df_coordinate = self.get_nwp_parameter(provider, parameter, coordinate)
263
+ df_coordinate = self.get_nwp_parameter(
264
+ provider,
265
+ parameter,
266
+ coordinate,
267
+ )
241
268
  dfs.append(df_coordinate)
242
269
 
243
270
  df = pd.concat(dfs)
@@ -252,7 +279,19 @@ class NWPManager:
252
279
  neighborhood: Neighborhood,
253
280
  ) -> pd.DataFrame:
254
281
  """
255
- Get weather features for a given coordinate.
282
+ Get NWP data for multiple parameters over a neighborhood.
283
+
284
+ For each parameter, retrieves the average NWP data in the neighborhood, then
285
+ merges all results on the time_utc column.
286
+
287
+ Args:
288
+ provider (NWPProvider): The NWP data provider to use.
289
+ parameters (list[NWPParameter]): List of NWP parameters to retrieve.
290
+ neighborhood (Neighborhood): The neighborhood specifying the central
291
+ coordinate and number of neighbors.
292
+
293
+ Returns:
294
+ pd.DataFrame: DataFrame with merged parameter values, indexed by time_utc.
256
295
  """
257
296
  dfs = []
258
297
  for parameter in parameters:
@@ -22,7 +22,7 @@ class OutageManager:
22
22
 
23
23
  df_outage = df_outage[~df_outage["is_unapproved"]]
24
24
 
25
- df_outage = df_outage.dropna(subset="plant_gsrn")
25
+ df_outage = df_outage.dropna(subset=["plant_gsrn"])
26
26
 
27
27
  # make placeholder dataframe to explode outage
28
28
  # data to all time points for all plants
@@ -34,12 +34,12 @@ class OutageManager:
34
34
 
35
35
  df_idx = df_times.merge(df_plants, how="cross")
36
36
 
37
- df_outage = pdz.merge(
37
+ df_outage = pdz.merge_point_interval(
38
38
  df_idx,
39
39
  df_outage,
40
40
  on=["plant_gsrn"],
41
- left_time="time_utc",
42
- right_period=("start_time_utc", "end_time_utc"),
41
+ left_point="time_utc",
42
+ right_interval=("start_time_utc", "end_time_utc"),
43
43
  )
44
44
 
45
45
  df_outage = df_outage.filter(
@@ -72,11 +72,11 @@ class PlantManager(MasterdataManager):
72
72
  df_plant = self.get_operational_entities("masterdataPlant")
73
73
  df_plant = df_plant.query(f"masterdata_gsrn == '{gsrn}'")
74
74
 
75
- df_plant = pdz.merge(
75
+ df_plant = pdz.merge_point_interval(
76
76
  df_times,
77
77
  df_plant,
78
- left_time="time_utc",
79
- right_period=("valid_from_date_utc", "valid_to_date_utc"),
78
+ left_point="time_utc",
79
+ right_interval=("valid_from_date_utc", "valid_to_date_utc"),
80
80
  )
81
81
 
82
82
  return df_plant.filter(["time_utc", "installed_power_MW"]).reset_index(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: domainiac
3
- Version: 9.2.0
3
+ Version: 9.3.1
4
4
  Summary: Package for working with Energinet data, but with specialized functions used for Enigma.
5
5
  Author: Team Enigma
6
6
  Author-email: enigma@energinet.dk
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Classifier: Programming Language :: Python :: 3.14
14
- Requires-Dist: datamazing (>=5.1.6)
14
+ Requires-Dist: datamazing (>=6.0.0)
15
15
  Requires-Dist: pandas (>=2.2.0)
16
16
  Requires-Dist: pvlib (>=0.13.1)
17
17
  Requires-Dist: scikit-learn (>=1.3.0)
@@ -1,4 +1,4 @@
1
- domainiac/__init__.py,sha256=Wuix4jscZD4sy7YB0RAedAo_eiRW5AcPlef58lKvg6s,207
1
+ domainiac/__init__.py,sha256=Xaeq4K8dcStlSilXiL6Sr0fCNgtPgWCKi93n8Cu8Cjw,232
2
2
  domainiac/functions/__init__.py,sha256=ZHNbj1aWRnUgM6TAlQqGLv2r-GdAvoaTrPIin1w5Dk0,137
3
3
  domainiac/functions/conversions.py,sha256=RZU5cpHD4uBHeqFfiFplJCv5gN-xzEHvQCKrizi1hMA,490
4
4
  domainiac/functions/interpolation.py,sha256=qj-sTEbez_IcXLY7eOS_KqAAd8GbhP5JBkdWrUPjWIQ,3720
@@ -6,12 +6,13 @@ domainiac/functions/solar.py,sha256=nU9ivDLA4gE0CTMttCb_7ztoQUQicWNe_11ZqVC7d-Y,
6
6
  domainiac/functions/temperature.py,sha256=-t_zWYIhYwJpyZEQgwxgR9SkeYAAjvb6qeV8LtDtfmk,940
7
7
  domainiac/functions/typing.py,sha256=gOvHUYIWSEv_aMttY1nMEyh-pdQKMJ8O31x9y47-U5U,229
8
8
  domainiac/functions/wind.py,sha256=ZOxSMxjWV7LZsAQVW5Y-GPMhZzfvIsh3NOtSXB5lUAY,993
9
- domainiac/managers/__init__.py,sha256=7N-iEsFidupPi1u3EURfeWAoQ-NdrzAwR13faA_VWUs,298
9
+ domainiac/managers/__init__.py,sha256=A4KdK_LiukXKlJLIF1hf8HF7xh3QeFdBqAWstXc8CgI,352
10
+ domainiac/managers/availability_manager.py,sha256=zKRjZlG5wp3MlviIbNg3s5da9nUKjP7_r8R5g8hHRg4,1895
10
11
  domainiac/managers/masterdata_manager.py,sha256=_xIHEWVYssdvC2VMujvR0ANXCI_Er3qOlfAX427yimk,2298
11
12
  domainiac/managers/metering_manager.py,sha256=MauMncY_yaVPx8Y9CCD8nJiE1BGHFKTVC1bOtiyl2pI,1701
12
- domainiac/managers/nwp_manager.py,sha256=rEN9HmDe01N5QzePGsUorWgmOuY7t9vMwBF1Kuuu_38,8603
13
- domainiac/managers/outage_manager.py,sha256=rH4NYY0hiD51V6SadVgtIOX0qCjG2cSZ60F9lDjkJ8o,1416
14
- domainiac/managers/plant_manager.py,sha256=aqJ04QB-QI83zIllQiyTrlh_SKR6CYI4bY4z4RlBL3g,5083
13
+ domainiac/managers/nwp_manager.py,sha256=h2J6ryXqplB_W6jC7aSenOa9MOxsYrd2HenU3WZnyu0,10040
14
+ domainiac/managers/outage_manager.py,sha256=KnBjsDFX8xQvA2UGh7gX2fTVwoQlF6PFoy5Uk_s3kfs,1436
15
+ domainiac/managers/plant_manager.py,sha256=Bh1jPwepDq_yO29QVZndjCXe3ScQndIsKwFZnjr9a8w,5101
15
16
  domainiac/managers/resource_manager.py,sha256=QMkNcrgyXLLlkCR9kg4-nFjTaYHtlsldex-b_8lAmO0,3730
16
17
  domainiac/managers/unit_manager.py,sha256=DYF-AiBHzUGZJE5XlTLcszvgIQkSzZs_s9o35l4bkVk,1219
17
18
  domainiac/modeling/__init__.py,sha256=Ac5feYZ9gVLayU1Cb1ACqOyDrmvcxoOvZuUkOeXVKf8,101
@@ -19,6 +20,6 @@ domainiac/modeling/nwp.py,sha256=PolrBdQn8W-e1M0_pefvmLn2mr4HT0NqlYlkyCV0dds,438
19
20
  domainiac/modeling/plant.py,sha256=Y0_Q6V6Lj3irJh5z-49r5gFJdD4Xl_pfHIMUhdaJVUI,2226
20
21
  domainiac/wrappers/__init__.py,sha256=vZOw9maXgVoAvudZqioD-GTPkgPI6fm7_CUELQcR_-g,43
21
22
  domainiac/wrappers/cache_wrapper.py,sha256=jDg-Lt_y-YzItyP-74tGULOxb07s_CcutmOHP1Ie00Q,355
22
- domainiac-9.2.0.dist-info/METADATA,sha256=_07orTSIon0JOGS6raqGn476L0GGh65-uNd8cksbx7g,737
23
- domainiac-9.2.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
24
- domainiac-9.2.0.dist-info/RECORD,,
23
+ domainiac-9.3.1.dist-info/METADATA,sha256=MonbkoqmH-AXby94IcR3J04hcDP9wiqeD3EUEM3hWlA,737
24
+ domainiac-9.3.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
25
+ domainiac-9.3.1.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any