detquantlib 3.15.0__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.0 → detquantlib-4.0.0}/PKG-INFO +7 -1
- {detquantlib-3.15.0 → detquantlib-4.0.0}/README.md +6 -0
- detquantlib-4.0.0/detquantlib/assets/__init__.py +4 -0
- detquantlib-4.0.0/detquantlib/assets/wind_turbine.py +345 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/outputs/outputs_interface.py +5 -12
- {detquantlib-3.15.0 → detquantlib-4.0.0}/pyproject.toml +1 -1
- detquantlib-3.15.0/detquantlib/assets/__init__.py +0 -4
- detquantlib-3.15.0/detquantlib/assets/wind_turbine.py +0 -92
- {detquantlib-3.15.0 → detquantlib-4.0.0}/LICENSE.txt +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/assets/battery.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/converters/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/converters/definitions.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/converters/energy.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/converters/helpers.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/converters/price.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/data/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/data/databases/helpers.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/dates/dates.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/forecasting/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/forecasting/forecasting.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/utils/logging.py +0 -0
- {detquantlib-3.15.0 → detquantlib-4.0.0}/detquantlib/utils/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detquantlib
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: An internal library containing functions and classes that can be used across Quant models.
|
|
5
5
|
License-File: LICENSE.txt
|
|
6
6
|
Author: Dynamic Energy Trading
|
|
@@ -80,6 +80,9 @@ poetry run python docs.py
|
|
|
80
80
|
|
|
81
81
|
Classes:
|
|
82
82
|
|
|
83
|
+
- `Battery`:
|
|
84
|
+
- `from detquantlib.assets import Battery`
|
|
85
|
+
- `from detquantlib.assets.battery import Battery`
|
|
83
86
|
- `DetDatabase`:
|
|
84
87
|
- `from detquantlib.data import DetDatabase`
|
|
85
88
|
- `from detquantlib.data.databases.detdatabase import DetDatabase`
|
|
@@ -98,6 +101,9 @@ Classes:
|
|
|
98
101
|
- `Sftp`:
|
|
99
102
|
- `from detquantlib.data import Sftp`
|
|
100
103
|
- `from detquantlib.data.sftp.sftp import Sftp`
|
|
104
|
+
- `WindTurbine`:
|
|
105
|
+
- `from detquantlib.assets import WindTurbine`
|
|
106
|
+
- `from detquantlib.assets.wind_turbine import WindTurbine`
|
|
101
107
|
|
|
102
108
|
Functions:
|
|
103
109
|
|
|
@@ -54,6 +54,9 @@ poetry run python docs.py
|
|
|
54
54
|
|
|
55
55
|
Classes:
|
|
56
56
|
|
|
57
|
+
- `Battery`:
|
|
58
|
+
- `from detquantlib.assets import Battery`
|
|
59
|
+
- `from detquantlib.assets.battery import Battery`
|
|
57
60
|
- `DetDatabase`:
|
|
58
61
|
- `from detquantlib.data import DetDatabase`
|
|
59
62
|
- `from detquantlib.data.databases.detdatabase import DetDatabase`
|
|
@@ -72,6 +75,9 @@ Classes:
|
|
|
72
75
|
- `Sftp`:
|
|
73
76
|
- `from detquantlib.data import Sftp`
|
|
74
77
|
- `from detquantlib.data.sftp.sftp import Sftp`
|
|
78
|
+
- `WindTurbine`:
|
|
79
|
+
- `from detquantlib.assets import WindTurbine`
|
|
80
|
+
- `from detquantlib.assets.wind_turbine import WindTurbine`
|
|
75
81
|
|
|
76
82
|
Functions:
|
|
77
83
|
|
|
@@ -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
|
+
)
|
|
@@ -184,26 +184,19 @@ class OutputSet:
|
|
|
184
184
|
class PathDefinitions:
|
|
185
185
|
"""A class containing hard-coded path definitions."""
|
|
186
186
|
|
|
187
|
-
@staticmethod
|
|
188
|
-
def default_outputs_folder_name():
|
|
189
|
-
return "Outputs"
|
|
190
|
-
|
|
191
187
|
@staticmethod
|
|
192
188
|
def outputs_plotly_folder_name():
|
|
193
189
|
return "PlotlyFigures"
|
|
194
190
|
|
|
195
191
|
@staticmethod
|
|
196
|
-
def
|
|
197
|
-
|
|
198
|
-
folder_name = os.getenv("OUTPUTS_FOLDER")
|
|
199
|
-
if folder_name is None:
|
|
200
|
-
folder_name = PathDefinitions.default_outputs_folder_name()
|
|
201
|
-
return folder_name
|
|
192
|
+
def default_outputs_folder_dir():
|
|
193
|
+
return Path.cwd().joinpath("Outputs")
|
|
202
194
|
|
|
203
195
|
@staticmethod
|
|
204
196
|
def get_outputs_folder_dir():
|
|
205
|
-
#
|
|
206
|
-
|
|
197
|
+
# Get path from environment variable if it exists, otherwise use default path
|
|
198
|
+
output_dir = os.getenv("OUTPUT_DIR")
|
|
199
|
+
return Path(output_dir) if output_dir else PathDefinitions.default_outputs_folder_dir()
|
|
207
200
|
|
|
208
201
|
@staticmethod
|
|
209
202
|
def get_outputs_plotly_folder_dir():
|
|
@@ -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
|