detquantlib 3.15.1__tar.gz → 4.0.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-3.15.1 → detquantlib-4.0.0}/PKG-INFO +1 -1
- detquantlib-4.0.0/detquantlib/assets/__init__.py +4 -0
- detquantlib-4.0.0/detquantlib/assets/wind_turbine.py +345 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/pyproject.toml +1 -1
- detquantlib-3.15.1/detquantlib/assets/__init__.py +0 -4
- detquantlib-3.15.1/detquantlib/assets/wind_turbine.py +0 -92
- {detquantlib-3.15.1 → detquantlib-4.0.0}/LICENSE.txt +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/README.md +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/assets/battery.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/converters/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/converters/definitions.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/converters/energy.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/converters/helpers.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/converters/price.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/data/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/data/databases/helpers.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/dates/dates.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/forecasting/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/forecasting/forecasting.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/utils/logging.py +0 -0
- {detquantlib-3.15.1 → detquantlib-4.0.0}/detquantlib/utils/utils.py +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Third-party packages
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
from scipy.interpolate import CubicSpline
|
|
5
|
+
|
|
6
|
+
# Internal modules
|
|
7
|
+
from detquantlib.converters import power_to_energy
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WindTurbine:
|
|
11
|
+
"""A class to store wind turbine information and perform wind turbine-related calculations."""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
rated_power_mw: float,
|
|
16
|
+
cut_in: float,
|
|
17
|
+
cut_out: float,
|
|
18
|
+
nr_turbines: int,
|
|
19
|
+
power_curve: CubicSpline = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Constructor method.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
rated_power_mw: Rated power of a single wind turbine (in MW)
|
|
26
|
+
cut_in: Cut-in wind speed
|
|
27
|
+
cut_out: Cut-out wind speed
|
|
28
|
+
nr_turbines: Number of wind turbines in the wind farm
|
|
29
|
+
power_curve: Power curve of a single wind turbine, stored as a cubic spline object.
|
|
30
|
+
If not provided, can be set via the 'fit_power_curve()' method below.
|
|
31
|
+
"""
|
|
32
|
+
self.rated_power_mw = rated_power_mw
|
|
33
|
+
self.cut_in = cut_in
|
|
34
|
+
self.cut_out = cut_out
|
|
35
|
+
self.nr_turbines = nr_turbines
|
|
36
|
+
self.power_curve = power_curve
|
|
37
|
+
|
|
38
|
+
def fit_power_curve(
|
|
39
|
+
self, wind_speed: list | np.ndarray | pd.Series, power_mw: list | np.ndarray | pd.Series
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Fits the wind turbine's power curve with a cubic spline, and stores the resulting spline
|
|
43
|
+
object in attribute self.power_curve.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
wind_speed: Wind speeds
|
|
47
|
+
power_mw: Corresponding power in MW (for a single wind turbine)
|
|
48
|
+
"""
|
|
49
|
+
# Make sure input data is stored as numpy array
|
|
50
|
+
wind_speed, power_mw = np.array(wind_speed), np.array(power_mw)
|
|
51
|
+
|
|
52
|
+
# Trim data to cut-in - cut-out interval
|
|
53
|
+
idx = (wind_speed >= self.cut_in) & (wind_speed <= self.cut_out)
|
|
54
|
+
wind_speed_trim, power_trim = wind_speed[idx], power_mw[idx]
|
|
55
|
+
|
|
56
|
+
# Add data point with power=0.0 before cut-in to ensure smooth behaviour of spline
|
|
57
|
+
# interpolation
|
|
58
|
+
w0 = wind_speed_trim[0] - np.diff(wind_speed_trim[:2])
|
|
59
|
+
p0 = [0]
|
|
60
|
+
wind_speed_trim, power_trim = np.concat([w0, wind_speed_trim]), np.concat([p0, power_trim])
|
|
61
|
+
|
|
62
|
+
# Fit cubic spline
|
|
63
|
+
cs = CubicSpline(wind_speed_trim, power_trim)
|
|
64
|
+
|
|
65
|
+
# Store spline object
|
|
66
|
+
self.power_curve = cs
|
|
67
|
+
|
|
68
|
+
def wind_to_power(self, wind_speed: list | np.ndarray | pd.Series) -> np.ndarray:
|
|
69
|
+
"""
|
|
70
|
+
Converts wind speeds to generated power, aggregated over the total number of turbines.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
wind_speed: Wind speeds
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Corresponding power in MW (total over the number of turbines set in the object)
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: Raises an error if self.power_curve is None.
|
|
80
|
+
"""
|
|
81
|
+
# Check that power curve attribute is defined
|
|
82
|
+
if self.power_curve is None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Cannot execute method self.wind_to_power() if attribute self.power_curve is "
|
|
85
|
+
"None. Use method self.fit_power_curve() to set self.power_curve based on power "
|
|
86
|
+
"curve data."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Make sure input data is stored as numpy array
|
|
90
|
+
wind_speed = np.array(wind_speed)
|
|
91
|
+
|
|
92
|
+
# Cubic spline interpolation
|
|
93
|
+
power_mw = self.power_curve(wind_speed)
|
|
94
|
+
|
|
95
|
+
# Cap to rated power
|
|
96
|
+
power_mw = np.minimum(power_mw, self.rated_power_mw)
|
|
97
|
+
|
|
98
|
+
# Adjust values outside of cut-in - cut-out bounds
|
|
99
|
+
idx = (wind_speed < self.cut_in) | (wind_speed > self.cut_out)
|
|
100
|
+
power_mw[idx] = 0.0
|
|
101
|
+
|
|
102
|
+
# Rescale power to number of wind turbines
|
|
103
|
+
power_mw *= self.nr_turbines
|
|
104
|
+
|
|
105
|
+
return power_mw
|
|
106
|
+
|
|
107
|
+
def wind_to_energy(self, wind_speed: list | np.ndarray | pd.Series, tenor: str) -> np.ndarray:
|
|
108
|
+
"""
|
|
109
|
+
Converts wind speeds to generated energy, aggregated over the total number of turbines.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
wind_speed: Wind speeds
|
|
113
|
+
tenor: Product tenor
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Corresponding energy in MWh (total over the number of turbines set in the object)
|
|
117
|
+
"""
|
|
118
|
+
power_mw = self.wind_to_power(wind_speed)
|
|
119
|
+
energy_mwh = power_to_energy(
|
|
120
|
+
power=power_mw, power_unit="MW", energy_unit="MWh", tenor=tenor
|
|
121
|
+
)
|
|
122
|
+
return energy_mwh
|
|
123
|
+
|
|
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
|
+
|
|
155
|
+
class WindTurbineModels:
|
|
156
|
+
"""
|
|
157
|
+
An inventory of known wind turbine models. Each model is stored as an object attribute. All
|
|
158
|
+
attribute dictionaries must have the same structure.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
enercon_e126_ep3_4mw = dict(
|
|
162
|
+
brand="Enercon",
|
|
163
|
+
model="E-126 EP3 4.0MW",
|
|
164
|
+
rated_power_mw=4.0,
|
|
165
|
+
cut_in=2.5,
|
|
166
|
+
cut_out=25.0,
|
|
167
|
+
power_curve_source="https://www.thewindpower.net/turbine_en_1576_enercon_e126-4000.php",
|
|
168
|
+
power_curve=[
|
|
169
|
+
{"wind_speed_(m/s)": 0.0, "power(mw)": 0.0},
|
|
170
|
+
{"wind_speed_(m/s)": 0.5, "power(mw)": 0.0},
|
|
171
|
+
{"wind_speed_(m/s)": 1.0, "power(mw)": 0.0},
|
|
172
|
+
{"wind_speed_(m/s)": 1.5, "power(mw)": 0.0},
|
|
173
|
+
{"wind_speed_(m/s)": 2.0, "power(mw)": 0.0},
|
|
174
|
+
{"wind_speed_(m/s)": 2.5, "power(mw)": 0.02},
|
|
175
|
+
{"wind_speed_(m/s)": 3.0, "power(mw)": 0.047},
|
|
176
|
+
{"wind_speed_(m/s)": 3.5, "power(mw)": 0.095},
|
|
177
|
+
{"wind_speed_(m/s)": 4.0, "power(mw)": 0.15},
|
|
178
|
+
{"wind_speed_(m/s)": 4.5, "power(mw)": 0.29},
|
|
179
|
+
{"wind_speed_(m/s)": 5.0, "power(mw)": 0.441},
|
|
180
|
+
{"wind_speed_(m/s)": 5.5, "power(mw)": 0.582},
|
|
181
|
+
{"wind_speed_(m/s)": 6.0, "power(mw)": 0.732},
|
|
182
|
+
{"wind_speed_(m/s)": 6.5, "power(mw)": 0.945},
|
|
183
|
+
{"wind_speed_(m/s)": 7.0, "power(mw)": 1.165},
|
|
184
|
+
{"wind_speed_(m/s)": 7.5, "power(mw)": 1.43},
|
|
185
|
+
{"wind_speed_(m/s)": 8.0, "power(mw)": 1.7},
|
|
186
|
+
{"wind_speed_(m/s)": 8.5, "power(mw)": 2.012},
|
|
187
|
+
{"wind_speed_(m/s)": 9.0, "power(mw)": 2.323},
|
|
188
|
+
{"wind_speed_(m/s)": 9.5, "power(mw)": 2.677},
|
|
189
|
+
{"wind_speed_(m/s)": 10.0, "power(mw)": 3.023},
|
|
190
|
+
{"wind_speed_(m/s)": 10.5, "power(mw)": 3.27},
|
|
191
|
+
{"wind_speed_(m/s)": 11.0, "power(mw)": 3.504},
|
|
192
|
+
{"wind_speed_(m/s)": 11.5, "power(mw)": 3.698},
|
|
193
|
+
{"wind_speed_(m/s)": 12.0, "power(mw)": 3.88},
|
|
194
|
+
{"wind_speed_(m/s)": 12.5, "power(mw)": 3.917},
|
|
195
|
+
{"wind_speed_(m/s)": 13.0, "power(mw)": 3.945},
|
|
196
|
+
{"wind_speed_(m/s)": 13.5, "power(mw)": 3.959},
|
|
197
|
+
{"wind_speed_(m/s)": 14.0, "power(mw)": 3.969},
|
|
198
|
+
{"wind_speed_(m/s)": 14.5, "power(mw)": 3.995},
|
|
199
|
+
{"wind_speed_(m/s)": 15.0, "power(mw)": 4.0},
|
|
200
|
+
{"wind_speed_(m/s)": 15.5, "power(mw)": 4.0},
|
|
201
|
+
{"wind_speed_(m/s)": 16.0, "power(mw)": 4.0},
|
|
202
|
+
{"wind_speed_(m/s)": 16.5, "power(mw)": 4.0},
|
|
203
|
+
{"wind_speed_(m/s)": 17.0, "power(mw)": 4.0},
|
|
204
|
+
{"wind_speed_(m/s)": 17.5, "power(mw)": 4.0},
|
|
205
|
+
{"wind_speed_(m/s)": 18.0, "power(mw)": 4.0},
|
|
206
|
+
{"wind_speed_(m/s)": 18.5, "power(mw)": 4.0},
|
|
207
|
+
{"wind_speed_(m/s)": 19.0, "power(mw)": 4.0},
|
|
208
|
+
{"wind_speed_(m/s)": 19.5, "power(mw)": 4.0},
|
|
209
|
+
{"wind_speed_(m/s)": 20.0, "power(mw)": 4.0},
|
|
210
|
+
{"wind_speed_(m/s)": 20.5, "power(mw)": 4.0},
|
|
211
|
+
{"wind_speed_(m/s)": 21.0, "power(mw)": 4.0},
|
|
212
|
+
{"wind_speed_(m/s)": 21.5, "power(mw)": 4.0},
|
|
213
|
+
{"wind_speed_(m/s)": 22.0, "power(mw)": 4.0},
|
|
214
|
+
{"wind_speed_(m/s)": 22.5, "power(mw)": 3.985},
|
|
215
|
+
{"wind_speed_(m/s)": 23.0, "power(mw)": 3.96},
|
|
216
|
+
{"wind_speed_(m/s)": 23.5, "power(mw)": 3.96},
|
|
217
|
+
{"wind_speed_(m/s)": 24.0, "power(mw)": 3.952},
|
|
218
|
+
{"wind_speed_(m/s)": 24.5, "power(mw)": 3.93},
|
|
219
|
+
{"wind_speed_(m/s)": 25.0, "power(mw)": 3.898},
|
|
220
|
+
],
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
enercon_e53_800 = dict(
|
|
224
|
+
brand="Enercon",
|
|
225
|
+
model="E-53 800kW",
|
|
226
|
+
rated_power_mw=0.81,
|
|
227
|
+
cut_in=2.0,
|
|
228
|
+
cut_out=25.0,
|
|
229
|
+
power_curve_source="https://www.thewindpower.net/turbine_en_4_enercon_e53-800.php",
|
|
230
|
+
power_curve=[
|
|
231
|
+
{"wind_speed_(m/s)": 0.0, "power(mw)": 0.0},
|
|
232
|
+
{"wind_speed_(m/s)": 0.5, "power(mw)": 0.0},
|
|
233
|
+
{"wind_speed_(m/s)": 1.0, "power(mw)": 0.0},
|
|
234
|
+
{"wind_speed_(m/s)": 1.5, "power(mw)": 0.0},
|
|
235
|
+
{"wind_speed_(m/s)": 2.0, "power(mw)": 0.002},
|
|
236
|
+
{"wind_speed_(m/s)": 2.5, "power(mw)": 0.008},
|
|
237
|
+
{"wind_speed_(m/s)": 3.0, "power(mw)": 0.014},
|
|
238
|
+
{"wind_speed_(m/s)": 3.5, "power(mw)": 0.026},
|
|
239
|
+
{"wind_speed_(m/s)": 4.0, "power(mw)": 0.038},
|
|
240
|
+
{"wind_speed_(m/s)": 4.5, "power(mw)": 0.057},
|
|
241
|
+
{"wind_speed_(m/s)": 5.0, "power(mw)": 0.077},
|
|
242
|
+
{"wind_speed_(m/s)": 5.5, "power(mw)": 0.109},
|
|
243
|
+
{"wind_speed_(m/s)": 6.0, "power(mw)": 0.141},
|
|
244
|
+
{"wind_speed_(m/s)": 6.5, "power(mw)": 0.185},
|
|
245
|
+
{"wind_speed_(m/s)": 7.0, "power(mw)": 0.228},
|
|
246
|
+
{"wind_speed_(m/s)": 7.5, "power(mw)": 0.282},
|
|
247
|
+
{"wind_speed_(m/s)": 8.0, "power(mw)": 0.336},
|
|
248
|
+
{"wind_speed_(m/s)": 8.5, "power(mw)": 0.408},
|
|
249
|
+
{"wind_speed_(m/s)": 9.0, "power(mw)": 0.48},
|
|
250
|
+
{"wind_speed_(m/s)": 9.5, "power(mw)": 0.562},
|
|
251
|
+
{"wind_speed_(m/s)": 10.0, "power(mw)": 0.645},
|
|
252
|
+
{"wind_speed_(m/s)": 10.5, "power(mw)": 0.71},
|
|
253
|
+
{"wind_speed_(m/s)": 11.0, "power(mw)": 0.744},
|
|
254
|
+
{"wind_speed_(m/s)": 11.5, "power(mw)": 0.765},
|
|
255
|
+
{"wind_speed_(m/s)": 12.0, "power(mw)": 0.785},
|
|
256
|
+
{"wind_speed_(m/s)": 12.5, "power(mw)": 0.802},
|
|
257
|
+
{"wind_speed_(m/s)": 13.0, "power(mw)": 0.81},
|
|
258
|
+
{"wind_speed_(m/s)": 13.5, "power(mw)": 0.81},
|
|
259
|
+
{"wind_speed_(m/s)": 14.0, "power(mw)": 0.81},
|
|
260
|
+
{"wind_speed_(m/s)": 14.5, "power(mw)": 0.81},
|
|
261
|
+
{"wind_speed_(m/s)": 15.0, "power(mw)": 0.81},
|
|
262
|
+
{"wind_speed_(m/s)": 15.5, "power(mw)": 0.81},
|
|
263
|
+
{"wind_speed_(m/s)": 16.0, "power(mw)": 0.81},
|
|
264
|
+
{"wind_speed_(m/s)": 16.5, "power(mw)": 0.81},
|
|
265
|
+
{"wind_speed_(m/s)": 17.0, "power(mw)": 0.81},
|
|
266
|
+
{"wind_speed_(m/s)": 17.5, "power(mw)": 0.81},
|
|
267
|
+
{"wind_speed_(m/s)": 18.0, "power(mw)": 0.81},
|
|
268
|
+
{"wind_speed_(m/s)": 18.5, "power(mw)": 0.81},
|
|
269
|
+
{"wind_speed_(m/s)": 19.0, "power(mw)": 0.81},
|
|
270
|
+
{"wind_speed_(m/s)": 19.5, "power(mw)": 0.81},
|
|
271
|
+
{"wind_speed_(m/s)": 20.0, "power(mw)": 0.81},
|
|
272
|
+
{"wind_speed_(m/s)": 20.5, "power(mw)": 0.81},
|
|
273
|
+
{"wind_speed_(m/s)": 21.0, "power(mw)": 0.81},
|
|
274
|
+
{"wind_speed_(m/s)": 21.5, "power(mw)": 0.81},
|
|
275
|
+
{"wind_speed_(m/s)": 22.0, "power(mw)": 0.81},
|
|
276
|
+
{"wind_speed_(m/s)": 22.5, "power(mw)": 0.81},
|
|
277
|
+
{"wind_speed_(m/s)": 23.0, "power(mw)": 0.81},
|
|
278
|
+
{"wind_speed_(m/s)": 23.5, "power(mw)": 0.81},
|
|
279
|
+
{"wind_speed_(m/s)": 24.0, "power(mw)": 0.81},
|
|
280
|
+
{"wind_speed_(m/s)": 24.5, "power(mw)": 0.81},
|
|
281
|
+
{"wind_speed_(m/s)": 25.0, "power(mw)": 0.81},
|
|
282
|
+
],
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
vestas_v52_850 = dict(
|
|
286
|
+
brand="Vestas",
|
|
287
|
+
model="V52 850kW",
|
|
288
|
+
rated_power_mw=0.85,
|
|
289
|
+
cut_in=3.0,
|
|
290
|
+
cut_out=25.0,
|
|
291
|
+
power_curve_source="https://www.thewindpower.net/turbine_en_27_vestas_v52-850.php",
|
|
292
|
+
power_curve=[
|
|
293
|
+
{"wind_speed_(m/s)": 0.0, "power(mw)": 0.0},
|
|
294
|
+
{"wind_speed_(m/s)": 0.5, "power(mw)": 0.0},
|
|
295
|
+
{"wind_speed_(m/s)": 1.0, "power(mw)": 0.0},
|
|
296
|
+
{"wind_speed_(m/s)": 1.5, "power(mw)": 0.0},
|
|
297
|
+
{"wind_speed_(m/s)": 2.0, "power(mw)": 0.0},
|
|
298
|
+
{"wind_speed_(m/s)": 2.5, "power(mw)": 0.0},
|
|
299
|
+
{"wind_speed_(m/s)": 3.0, "power(mw)": 0.005},
|
|
300
|
+
{"wind_speed_(m/s)": 3.5, "power(mw)": 0.015},
|
|
301
|
+
{"wind_speed_(m/s)": 4.0, "power(mw)": 0.03},
|
|
302
|
+
{"wind_speed_(m/s)": 4.5, "power(mw)": 0.049},
|
|
303
|
+
{"wind_speed_(m/s)": 5.0, "power(mw)": 0.067},
|
|
304
|
+
{"wind_speed_(m/s)": 5.5, "power(mw)": 0.097},
|
|
305
|
+
{"wind_speed_(m/s)": 6.0, "power(mw)": 0.127},
|
|
306
|
+
{"wind_speed_(m/s)": 6.5, "power(mw)": 0.162},
|
|
307
|
+
{"wind_speed_(m/s)": 7.0, "power(mw)": 0.197},
|
|
308
|
+
{"wind_speed_(m/s)": 7.5, "power(mw)": 0.244},
|
|
309
|
+
{"wind_speed_(m/s)": 8.0, "power(mw)": 0.29},
|
|
310
|
+
{"wind_speed_(m/s)": 8.5, "power(mw)": 0.35},
|
|
311
|
+
{"wind_speed_(m/s)": 9.0, "power(mw)": 0.41},
|
|
312
|
+
{"wind_speed_(m/s)": 9.5, "power(mw)": 0.475},
|
|
313
|
+
{"wind_speed_(m/s)": 10.0, "power(mw)": 0.539},
|
|
314
|
+
{"wind_speed_(m/s)": 10.5, "power(mw)": 0.6},
|
|
315
|
+
{"wind_speed_(m/s)": 11.0, "power(mw)": 0.66},
|
|
316
|
+
{"wind_speed_(m/s)": 11.5, "power(mw)": 0.71},
|
|
317
|
+
{"wind_speed_(m/s)": 12.0, "power(mw)": 0.751},
|
|
318
|
+
{"wind_speed_(m/s)": 12.5, "power(mw)": 0.784},
|
|
319
|
+
{"wind_speed_(m/s)": 13.0, "power(mw)": 0.816},
|
|
320
|
+
{"wind_speed_(m/s)": 13.5, "power(mw)": 0.84},
|
|
321
|
+
{"wind_speed_(m/s)": 14.0, "power(mw)": 0.85},
|
|
322
|
+
{"wind_speed_(m/s)": 14.5, "power(mw)": 0.85},
|
|
323
|
+
{"wind_speed_(m/s)": 15.0, "power(mw)": 0.85},
|
|
324
|
+
{"wind_speed_(m/s)": 15.5, "power(mw)": 0.85},
|
|
325
|
+
{"wind_speed_(m/s)": 16.0, "power(mw)": 0.85},
|
|
326
|
+
{"wind_speed_(m/s)": 16.5, "power(mw)": 0.85},
|
|
327
|
+
{"wind_speed_(m/s)": 17.0, "power(mw)": 0.85},
|
|
328
|
+
{"wind_speed_(m/s)": 17.5, "power(mw)": 0.85},
|
|
329
|
+
{"wind_speed_(m/s)": 18.0, "power(mw)": 0.85},
|
|
330
|
+
{"wind_speed_(m/s)": 18.5, "power(mw)": 0.85},
|
|
331
|
+
{"wind_speed_(m/s)": 19.0, "power(mw)": 0.85},
|
|
332
|
+
{"wind_speed_(m/s)": 19.5, "power(mw)": 0.85},
|
|
333
|
+
{"wind_speed_(m/s)": 20.0, "power(mw)": 0.85},
|
|
334
|
+
{"wind_speed_(m/s)": 20.5, "power(mw)": 0.85},
|
|
335
|
+
{"wind_speed_(m/s)": 21.0, "power(mw)": 0.85},
|
|
336
|
+
{"wind_speed_(m/s)": 21.5, "power(mw)": 0.85},
|
|
337
|
+
{"wind_speed_(m/s)": 22.0, "power(mw)": 0.85},
|
|
338
|
+
{"wind_speed_(m/s)": 22.5, "power(mw)": 0.85},
|
|
339
|
+
{"wind_speed_(m/s)": 23.0, "power(mw)": 0.85},
|
|
340
|
+
{"wind_speed_(m/s)": 23.5, "power(mw)": 0.85},
|
|
341
|
+
{"wind_speed_(m/s)": 24.0, "power(mw)": 0.85},
|
|
342
|
+
{"wind_speed_(m/s)": 24.5, "power(mw)": 0.85},
|
|
343
|
+
{"wind_speed_(m/s)": 25.0, "power(mw)": 0.85},
|
|
344
|
+
],
|
|
345
|
+
)
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
# Third-party packages
|
|
2
|
-
import numpy as np
|
|
3
|
-
import pandas as pd
|
|
4
|
-
from scipy.interpolate import CubicSpline
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class WindTurbine:
|
|
8
|
-
"""A class to store wind turbine information and perform wind turbine-related calculations."""
|
|
9
|
-
|
|
10
|
-
def __init__(
|
|
11
|
-
self, rated_power: float, cut_in: float, cut_out: float, power_curve: CubicSpline = None
|
|
12
|
-
):
|
|
13
|
-
"""
|
|
14
|
-
Constructor method.
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
rated_power: Rated power
|
|
18
|
-
cut_in: Cut-in wind speed
|
|
19
|
-
cut_out: Cut-out wind speed
|
|
20
|
-
power_curve: Power curve, stored as a cubic spline object. If not provided, can be
|
|
21
|
-
set via the 'fit_power_curve()' method below.
|
|
22
|
-
"""
|
|
23
|
-
self.rated_power = rated_power
|
|
24
|
-
self.cut_in = cut_in
|
|
25
|
-
self.cut_out = cut_out
|
|
26
|
-
self.power_curve = power_curve
|
|
27
|
-
|
|
28
|
-
def fit_power_curve(
|
|
29
|
-
self, wind_speed: list | np.ndarray | pd.Series, power: list | np.ndarray | pd.Series
|
|
30
|
-
):
|
|
31
|
-
"""
|
|
32
|
-
Fits the wind turbine's power curve with a cubic spline, and stores the resulting spline
|
|
33
|
-
object in attribute self.power_curve.
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
wind_speed: Wind speeds
|
|
37
|
-
power: Corresponding power
|
|
38
|
-
"""
|
|
39
|
-
# Make sure input data is stored as numpy array
|
|
40
|
-
wind_speed, power = np.array(wind_speed), np.array(power)
|
|
41
|
-
|
|
42
|
-
# Trim data to cut-in - cut-out interval
|
|
43
|
-
idx = (wind_speed >= self.cut_in) & (wind_speed <= self.cut_out)
|
|
44
|
-
wind_speed_trim, power_trim = wind_speed[idx], power[idx]
|
|
45
|
-
|
|
46
|
-
# Add data point with power=0.0 before cut-in to ensure smooth behaviour of spline
|
|
47
|
-
# interpolation
|
|
48
|
-
w0 = wind_speed_trim[0] - np.diff(wind_speed_trim[:2])
|
|
49
|
-
p0 = [0]
|
|
50
|
-
wind_speed_trim, power_trim = np.concat([w0, wind_speed_trim]), np.concat([p0, power_trim])
|
|
51
|
-
|
|
52
|
-
# Fit cubic spline
|
|
53
|
-
cs = CubicSpline(wind_speed_trim, power_trim)
|
|
54
|
-
|
|
55
|
-
# Store spline object
|
|
56
|
-
self.power_curve = cs
|
|
57
|
-
|
|
58
|
-
def wind_to_power(self, wind_speed: list | np.ndarray | pd.Series) -> np.ndarray:
|
|
59
|
-
"""
|
|
60
|
-
Converts wind speeds to wind turbine power.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
wind_speed: Wind speeds
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Corresponding power
|
|
67
|
-
|
|
68
|
-
Raises:
|
|
69
|
-
ValueError: Raises an error if self.power_curve is None.
|
|
70
|
-
"""
|
|
71
|
-
# Check that power curve attribute is defined
|
|
72
|
-
if self.power_curve is None:
|
|
73
|
-
raise ValueError(
|
|
74
|
-
"Cannot execute method self.wind_to_power() if attribute self.power_curve is "
|
|
75
|
-
"None. Use method self.fit_power_curve() to set self.power_curve based on power "
|
|
76
|
-
"curve data."
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
# Make sure input data is stored as numpy array
|
|
80
|
-
wind_speed = np.array(wind_speed)
|
|
81
|
-
|
|
82
|
-
# Cubic spline interpolation
|
|
83
|
-
power = self.power_curve(wind_speed)
|
|
84
|
-
|
|
85
|
-
# Cap to rated power
|
|
86
|
-
power = np.minimum(power, self.rated_power)
|
|
87
|
-
|
|
88
|
-
# Adjust values outside of cut-in - cut-out bounds
|
|
89
|
-
idx = (wind_speed < self.cut_in) | (wind_speed > self.cut_out)
|
|
90
|
-
power[idx] = 0.0
|
|
91
|
-
|
|
92
|
-
return power
|
|
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
|