detquantlib 5.1.2__tar.gz → 5.3.0__tar.gz
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.
- {detquantlib-5.1.2 → detquantlib-5.3.0}/PKG-INFO +1 -1
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/assets/wind_turbine.py +70 -32
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/dates/dates.py +37 -2
- {detquantlib-5.1.2 → detquantlib-5.3.0}/pyproject.toml +1 -1
- {detquantlib-5.1.2 → detquantlib-5.3.0}/LICENSE.txt +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/README.md +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/assets/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/assets/battery.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/converters/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/converters/definitions.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/converters/energy.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/converters/helpers.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/converters/price.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/data/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/data/databases/helpers.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/forecasting/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/forecasting/forecasting.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/utils/logging.py +0 -0
- {detquantlib-5.1.2 → detquantlib-5.3.0}/detquantlib/utils/utils.py +0 -0
|
@@ -5,6 +5,7 @@ from scipy.interpolate import CubicSpline
|
|
|
5
5
|
|
|
6
6
|
# Internal modules
|
|
7
7
|
from detquantlib.converters import power_to_energy
|
|
8
|
+
from detquantlib.utils import list_to_str
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class WindTurbine:
|
|
@@ -35,6 +36,73 @@ class WindTurbine:
|
|
|
35
36
|
self.nr_turbines = nr_turbines
|
|
36
37
|
self.power_curve = power_curve
|
|
37
38
|
|
|
39
|
+
@staticmethod
|
|
40
|
+
def list_models() -> list[str]:
|
|
41
|
+
"""Gets the list of available wind turbine models."""
|
|
42
|
+
return [d for d in WindTurbineModels.__dict__ if not d.startswith("__")]
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def get_model(model_name: str) -> dict:
|
|
46
|
+
"""Gets the model information of a given wind turbine."""
|
|
47
|
+
try:
|
|
48
|
+
return getattr(WindTurbineModels, model_name)
|
|
49
|
+
except AttributeError as e:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"Invalid model name '{model_name}'. Available models: "
|
|
52
|
+
f"{list_to_str(WindTurbine.list_models())}."
|
|
53
|
+
) from e
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def build_from_model(model_name: str = None, model_info: dict = None, nr_turbines: int = 1):
|
|
57
|
+
"""
|
|
58
|
+
Builds a WindTurbine object from a known wind turbine model.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
model_name: Wind turbine model name, as given in the WindTurbineModels class. If not
|
|
62
|
+
provided, argument 'model_info' should be provided.
|
|
63
|
+
model_info: Wind turbine model information. The dictionary can be taken from the
|
|
64
|
+
list of models in the WindTurbineModels class. Otherwise, the dictionary should
|
|
65
|
+
have the same structure as those in the WindTurbineModels class. If not provided,
|
|
66
|
+
argument 'model_name' should be provided.
|
|
67
|
+
nr_turbines: Number of turbines in the wind farm
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
WindTurbine object
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ValueError: Raises an error if both 'model_name' and 'model_info' are missing.
|
|
74
|
+
ValueError: Raises an error if both 'model_name' and 'model_info' are provided.
|
|
75
|
+
"""
|
|
76
|
+
# Inputs validation
|
|
77
|
+
if model_name is None and model_info is None:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"Cannot determine turbine model. Please provide either argument 'model_name' "
|
|
80
|
+
"or 'model_info'."
|
|
81
|
+
)
|
|
82
|
+
elif model_name is not None and model_info is not None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Cannot determine turbine model. Please provide either argument 'model_name' "
|
|
85
|
+
"or 'model_info', but not both."
|
|
86
|
+
)
|
|
87
|
+
elif model_name is not None:
|
|
88
|
+
model_info = WindTurbine.get_model(model_name)
|
|
89
|
+
|
|
90
|
+
# Create object
|
|
91
|
+
obj = WindTurbine(
|
|
92
|
+
rated_power_mw=model_info["rated_power_mw"],
|
|
93
|
+
cut_in=model_info["cut_in"],
|
|
94
|
+
cut_out=model_info["cut_out"],
|
|
95
|
+
nr_turbines=nr_turbines,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Set power_curve attribute
|
|
99
|
+
obj.fit_power_curve(
|
|
100
|
+
wind_speed=[d["wind_speed_(m/s)"] for d in model_info["power_curve"]],
|
|
101
|
+
power_mw=[d["power(mw)"] for d in model_info["power_curve"]],
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return obj
|
|
105
|
+
|
|
38
106
|
def fit_power_curve(
|
|
39
107
|
self, wind_speed: list | np.ndarray | pd.Series, power_mw: list | np.ndarray | pd.Series
|
|
40
108
|
):
|
|
@@ -92,8 +160,8 @@ class WindTurbine:
|
|
|
92
160
|
# Cubic spline interpolation
|
|
93
161
|
power_mw = self.power_curve(wind_speed)
|
|
94
162
|
|
|
95
|
-
#
|
|
96
|
-
power_mw = np.
|
|
163
|
+
# Bound between 0 and rated power
|
|
164
|
+
power_mw = np.clip(a=power_mw, a_min=0.0, a_max=self.rated_power_mw)
|
|
97
165
|
|
|
98
166
|
# Adjust values outside of cut-in - cut-out bounds
|
|
99
167
|
idx = (wind_speed < self.cut_in) | (wind_speed > self.cut_out)
|
|
@@ -121,36 +189,6 @@ class WindTurbine:
|
|
|
121
189
|
)
|
|
122
190
|
return energy_mwh
|
|
123
191
|
|
|
124
|
-
@staticmethod
|
|
125
|
-
def build_from_model(model: dict, nr_turbines: int):
|
|
126
|
-
"""
|
|
127
|
-
Builds a WindTurbine object from a known wind turbine model.
|
|
128
|
-
|
|
129
|
-
Args:
|
|
130
|
-
model: Wind turbine model. The dictionary can be taken from the list of models in
|
|
131
|
-
the WindTurbineModels class. Otherwise, the dictionary should have the same
|
|
132
|
-
structure as those in the WindTurbineModels class.
|
|
133
|
-
nr_turbines: Number of wind turbines in the wind farm
|
|
134
|
-
|
|
135
|
-
Returns:
|
|
136
|
-
WindTurbine object
|
|
137
|
-
"""
|
|
138
|
-
# Create object
|
|
139
|
-
obj = WindTurbine(
|
|
140
|
-
rated_power_mw=model["rated_power_mw"],
|
|
141
|
-
cut_in=model["cut_in"],
|
|
142
|
-
cut_out=model["cut_out"],
|
|
143
|
-
nr_turbines=nr_turbines,
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
# Set power_curve attribute
|
|
147
|
-
obj.fit_power_curve(
|
|
148
|
-
wind_speed=[d["wind_speed_(m/s)"] for d in model["power_curve"]],
|
|
149
|
-
power_mw=[d["power(mw)"] for d in model["power_curve"]],
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
return obj
|
|
153
|
-
|
|
154
192
|
|
|
155
193
|
class WindTurbineModels:
|
|
156
194
|
"""
|
|
@@ -3,10 +3,45 @@ from datetime import datetime, timezone
|
|
|
3
3
|
from typing import Literal
|
|
4
4
|
|
|
5
5
|
# Third-party packages
|
|
6
|
-
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from dateutil.relativedelta import relativedelta
|
|
7
8
|
|
|
8
9
|
# Specify functions to expose
|
|
9
|
-
__all__ = ["count_nr_hours", "calc_months_diff", "datetime_to_month_code"]
|
|
10
|
+
__all__ = ["ts_ceil", "ts_floor", "count_nr_hours", "calc_months_diff", "datetime_to_month_code"]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def ts_ceil(ts: pd.Timestamp, freq: str) -> pd.Timestamp:
|
|
14
|
+
"""
|
|
15
|
+
Short wrapper function around the pd.Timestamp object.ceil() method that also supports
|
|
16
|
+
October DST timestamps (i.e. between 02:00:00+0200 and 03:00:00+0100), because the built-in
|
|
17
|
+
method raises an error in those cases.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
ts: Timestamp
|
|
21
|
+
freq: Frequency
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Ceiled timestamp
|
|
25
|
+
"""
|
|
26
|
+
local_tz = ts.tz
|
|
27
|
+
return ts.tz_convert("UTC").ceil(freq).tz_convert(local_tz)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ts_floor(ts: pd.Timestamp, freq: str) -> pd.Timestamp:
|
|
31
|
+
"""
|
|
32
|
+
Short wrapper function around the pd.Timestamp object.floor() method that also supports
|
|
33
|
+
October DST timestamps (i.e. between 02:00:00+0200 and 03:00:00+0100), because the built-in
|
|
34
|
+
method raises an error in those cases.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
ts: Timestamp
|
|
38
|
+
freq: Frequency
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Floored timestamp
|
|
42
|
+
"""
|
|
43
|
+
local_tz = ts.tz
|
|
44
|
+
return ts.tz_convert("UTC").floor(freq).tz_convert(local_tz)
|
|
10
45
|
|
|
11
46
|
|
|
12
47
|
def count_nr_hours(start_date: datetime, end_date: datetime) -> float:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|