Skip to content

Commit 96de09f

Browse files
authored
Merge pull request #112 from kyri-petrou/features/speedup_power_outputs
Use pure numpy arrays in power_output calculation (~x10 speedup for large datasets)
2 parents 2486d1e + 462923d commit 96de09f

File tree

2 files changed

+174
-89
lines changed

2 files changed

+174
-89
lines changed

tests/test_power_output.py

Lines changed: 129 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,25 @@ def setup_class(self):
2525
"power_coefficient_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]),
2626
"power_coefficient_curve_values": pd.Series([0.3, 0.4, 0.5]),
2727
}
28+
self.parameters2: Dict = {
29+
"wind_speed": pd.Series(data=[2.0, 5.5, 7.0]),
30+
"density": pd.Series(data=[1.3, 1.3, 1.3]),
31+
"density_correction": False,
32+
"power_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]),
33+
"power_curve_values": pd.Series([300, 400, 500]),
34+
}
35+
self.power_output_exp1 = pd.Series(
36+
data=[0.0, 450.0, 0.0], name="feedin_power_plant"
37+
)
38+
self.power_output_exp2 = pd.Series(
39+
data=[0.0, 461.00290572, 0.0], name="feedin_power_plant"
40+
)
2841

2942
def test_power_coefficient_curve_1(self):
30-
# Test wind_speed as pd.Series with density and power_coefficient_curve
31-
# as pd.Series and np.array
43+
"""
44+
Test wind_speed as pd.Series with density and power_coefficient_curve
45+
as pd.Series and np.array
46+
"""
3247
power_output_exp = pd.Series(
3348
data=[0.0, 244615.399, 0.0], name="feedin_power_plant"
3449
)
@@ -53,14 +68,20 @@ def test_power_coefficient_curve_1(self):
5368
)
5469

5570
def test_power_coefficient_curve_output_types(self):
56-
parameters = self.parameters
57-
# Test wind_speed as np.array with density and power_coefficient_curve
58-
# as np.array and pd.Series
59-
assert isinstance(power_coefficient_curve(**parameters), pd.Series)
60-
parameters["wind_speed"] = np.array(parameters["wind_speed"])
61-
assert isinstance(power_coefficient_curve(**parameters), np.ndarray)
71+
"""
72+
Test wind_speed as np.array with density and power_coefficient_curve
73+
as np.array and pd.Series
74+
"""
75+
assert isinstance(
76+
power_coefficient_curve(**self.parameters), pd.Series
77+
)
78+
self.parameters["wind_speed"] = np.array(self.parameters["wind_speed"])
79+
assert isinstance(
80+
power_coefficient_curve(**self.parameters), np.ndarray
81+
)
6282

6383
def test_power_coefficient_curve_2(self):
84+
"""TODO: Explain this test"""
6485
parameters = self.parameters
6586
power_output_exp = np.array([0.0, 244615.399, 0.0])
6687
parameters["wind_speed"] = np.array(parameters["wind_speed"])
@@ -83,86 +104,125 @@ def test_power_coefficient_curve_2(self):
83104
)
84105
assert isinstance(power_coefficient_curve(**parameters), np.ndarray)
85106

86-
def test_power_curve(self):
87-
parameters = {
88-
"wind_speed": pd.Series(data=[2.0, 5.5, 7.0]),
89-
"density": pd.Series(data=[1.3, 1.3, 1.3]),
90-
"density_correction": False,
91-
"power_curve_wind_speeds": pd.Series([4.0, 5.0, 6.0]),
92-
"power_curve_values": pd.Series([300, 400, 500]),
93-
}
94-
107+
def test_power_curve_1(self):
95108
# Tests without density correction:
96109
# Test wind_speed as pd.Series and power_curve as pd.Series and
97110
# np.array
98-
power_output_exp = pd.Series(
99-
data=[0.0, 450.0, 0.0], name="feedin_power_plant"
111+
112+
assert_series_equal(
113+
power_curve(**self.parameters2), self.power_output_exp1
100114
)
101-
assert_series_equal(power_curve(**parameters), power_output_exp)
102-
parameters["power_curve_values"] = np.array(
103-
parameters["power_curve_values"]
115+
116+
def test_power_curve_2(self):
117+
"""TODO: Explain this test"""
118+
self.parameters2["power_curve_values"] = np.array(
119+
self.parameters2["power_curve_values"]
104120
)
105-
parameters["power_curve_wind_speeds"] = np.array(
106-
parameters["power_curve_wind_speeds"]
121+
self.parameters2["power_curve_wind_speeds"] = np.array(
122+
self.parameters2["power_curve_wind_speeds"]
123+
)
124+
assert_series_equal(
125+
power_curve(**self.parameters2), self.power_output_exp1
107126
)
108-
assert_series_equal(power_curve(**parameters), power_output_exp)
109127

110-
# Test wind_speed as np.array and power_curve as pd.Series and np.array
111-
power_output_exp = np.array([0.0, 450.0, 0.0])
112-
parameters["wind_speed"] = np.array(parameters["wind_speed"])
113-
assert_allclose(power_curve(**parameters), power_output_exp)
114-
assert isinstance(power_curve(**parameters), np.ndarray)
115-
parameters["power_curve_wind_speeds"] = pd.Series(
116-
data=parameters["power_curve_wind_speeds"]
128+
def test_power_curve_3(self):
129+
"""
130+
Test wind_speed as np.array and power_curve as
131+
pd.Series and np.array
132+
"""
133+
power_output_exp = np.array(self.power_output_exp1)
134+
self.parameters2["wind_speed"] = np.array(
135+
self.parameters2["wind_speed"]
136+
)
137+
assert_allclose(power_curve(**self.parameters2), power_output_exp)
138+
assert isinstance(power_curve(**self.parameters2), np.ndarray)
139+
140+
def test_power_curve_4(self):
141+
"""TODO: Explain this test"""
142+
self.parameters2["power_curve_wind_speeds"] = pd.Series(
143+
data=self.parameters2["power_curve_wind_speeds"]
117144
)
118-
parameters["power_curve_values"] = pd.Series(
119-
data=parameters["power_curve_values"]
145+
self.parameters2["power_curve_values"] = pd.Series(
146+
data=self.parameters2["power_curve_values"]
120147
)
121-
assert_allclose(power_curve(**parameters), power_output_exp)
122-
assert isinstance(power_curve(**parameters), np.ndarray)
148+
assert_allclose(
149+
power_curve(**self.parameters2), self.power_output_exp1
150+
)
151+
assert isinstance(power_curve(**self.parameters2), np.ndarray)
123152

124-
# Tests with density correction:
125-
# Test wind_speed as np.array with density and power_curve as pd.Series
126-
# and np.array
127-
power_output_exp = np.array([0.0, 461.00290572, 0.0])
128-
parameters["density_correction"] = True
129-
assert_allclose(power_curve(**parameters), power_output_exp)
130-
assert isinstance(power_curve(**parameters), np.ndarray)
131-
parameters["density"] = np.array(parameters["density"])
132-
assert_allclose(power_curve(**parameters), power_output_exp)
133-
assert isinstance(power_curve(**parameters), np.ndarray)
134-
parameters["power_curve_values"] = np.array(
135-
parameters["power_curve_values"]
153+
def test_power_curve_5(self):
154+
"""
155+
Tests with density correction:
156+
Test wind_speed as np.array with density and power_curve as pd.Series
157+
and np.array
158+
"""
159+
power_output_exp = np.array(self.power_output_exp2)
160+
self.parameters2["density_correction"] = True
161+
assert_allclose(power_curve(**self.parameters2), power_output_exp)
162+
assert isinstance(power_curve(**self.parameters2), np.ndarray)
163+
164+
def test_power_curve_6(self):
165+
"""TODO: Explain this test"""
166+
self.parameters2["density"] = np.array(self.parameters2["density"])
167+
assert_allclose(
168+
power_curve(**self.parameters2), self.power_output_exp2
136169
)
137-
parameters["power_curve_wind_speeds"] = np.array(
138-
parameters["power_curve_wind_speeds"]
170+
assert isinstance(power_curve(**self.parameters2), np.ndarray)
171+
172+
def test_power_curve_7(self):
173+
"""TODO: Explain this test"""
174+
self.parameters2["power_curve_values"] = np.array(
175+
self.parameters2["power_curve_values"]
139176
)
140-
assert_allclose(power_curve(**parameters), power_output_exp)
141-
assert isinstance(power_curve(**parameters), np.ndarray)
177+
self.parameters2["power_curve_wind_speeds"] = np.array(
178+
self.parameters2["power_curve_wind_speeds"]
179+
)
180+
assert_allclose(
181+
power_curve(**self.parameters2), self.power_output_exp2
182+
)
183+
assert isinstance(power_curve(**self.parameters2), np.ndarray)
142184

143-
# Test wind_speed as pd.Series with density and power_curve as
144-
# np. array and pd.Series
145-
power_output_exp = pd.Series(
146-
data=[0.0, 461.00290572, 0.0], name="feedin_power_plant"
185+
def test_power_curve_8(self):
186+
"""
187+
Test wind_speed as pd.Series with density and power_curve as np. array
188+
and pd.Series
189+
"""
190+
self.parameters2["wind_speed"] = pd.Series(
191+
data=self.parameters2["wind_speed"]
147192
)
148-
parameters["wind_speed"] = pd.Series(data=parameters["wind_speed"])
149-
assert_series_equal(power_curve(**parameters), power_output_exp)
150-
parameters["density"] = pd.Series(data=parameters["density"])
151-
assert_series_equal(power_curve(**parameters), power_output_exp)
152-
parameters["power_curve_wind_speeds"] = pd.Series(
153-
data=parameters["power_curve_wind_speeds"]
193+
assert_series_equal(
194+
power_curve(**self.parameters2), self.power_output_exp2
154195
)
155-
parameters["power_curve_values"] = pd.Series(
156-
data=parameters["power_curve_values"]
196+
197+
def test_power_curve_9(self):
198+
"""TODO: Explain this test"""
199+
self.parameters2["density"] = pd.Series(
200+
data=self.parameters2["density"]
201+
)
202+
assert_series_equal(
203+
power_curve(**self.parameters2), self.power_output_exp2
157204
)
158-
assert_series_equal(power_curve(**parameters), power_output_exp)
159205

160-
# Raise TypeErrors due to wrong type of `density_correction`
161-
with pytest.raises(TypeError):
162-
parameters["density"] = "wrong_type"
163-
power_curve(**parameters)
206+
def test_power_curve_10(self):
207+
"""TODO: Explain this test"""
208+
self.parameters2["power_curve_wind_speeds"] = pd.Series(
209+
data=self.parameters2["power_curve_wind_speeds"]
210+
)
211+
self.parameters2["power_curve_values"] = pd.Series(
212+
data=self.parameters2["power_curve_values"]
213+
)
214+
assert_series_equal(
215+
power_curve(**self.parameters2), self.power_output_exp2
216+
)
217+
218+
def test_power_curve_11(self):
219+
"""Raise IndexErrors due to wrong type of `density_correction`"""
220+
with pytest.raises(IndexError, match="too many indices for array"):
221+
self.parameters2["density"] = "wrong_type"
222+
power_curve(**self.parameters2)
164223

165224
def test_power_curve_density_correction(self):
225+
"""TODO: Explain and split this test."""
166226
parameters = {
167227
"wind_speed": pd.Series(data=[2.0, 5.5, 7.0]),
168228
"density": pd.Series(data=[1.3, 1.3, 1.3]),

windpowerlib/power_output.py

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -247,24 +247,12 @@ def power_curve_density_correction(
247247
else:
248248
panda_series = False
249249

250-
power_output = [
251-
(
252-
np.interp(
253-
wind_speed[i],
254-
power_curve_wind_speeds
255-
* (1.225 / density[i])
256-
** (
257-
np.interp(
258-
power_curve_wind_speeds, [7.5, 12.5], [1 / 3, 2 / 3]
259-
)
260-
),
261-
power_curve_values,
262-
left=0,
263-
right=0,
264-
)
265-
)
266-
for i in range(len(wind_speed))
267-
]
250+
power_output = _get_power_output(
251+
wind_speed,
252+
np.array(power_curve_wind_speeds),
253+
np.array(density),
254+
np.array(power_curve_values),
255+
)
268256

269257
# Convert results to the data type of the input data
270258
if panda_series:
@@ -273,6 +261,43 @@ def power_curve_density_correction(
273261
index=wind_speed_indexes, # Use previously saved wind speed index
274262
name="feedin_power_plant",
275263
)
276-
else:
277-
power_output = np.array(power_output)
264+
265+
return power_output
266+
267+
268+
def _get_power_output(
269+
wind_speed, power_curve_wind_speeds, density, power_curve_values
270+
):
271+
"""Get the power output at each timestep using only numpy to speed up performance
272+
Parameters
273+
----------
274+
wind_speed : :numpy:`numpy.ndarray`
275+
Wind speed at hub height in m/s.
276+
power_curve_wind_speeds : :numpy:`numpy.ndarray`
277+
Wind speeds in m/s for which the power curve values are provided in
278+
`power_curve_values`.
279+
density : :numpy:`numpy.ndarray`
280+
Density of air at hub height in kg/m³.
281+
power_curve_values : :numpy:`numpy.ndarray`
282+
Power curve values corresponding to wind speeds in
283+
`power_curve_wind_speeds`.
284+
Returns
285+
-------
286+
:numpy:`numpy.array`
287+
Electrical power output of the wind turbine in W.
288+
"""
289+
power_output = np.empty(len(wind_speed), dtype=np.float)
290+
291+
for i in range(len(wind_speed)):
292+
power_output[i] = np.interp(
293+
wind_speed[i],
294+
power_curve_wind_speeds
295+
* (1.225 / density[i])
296+
** (
297+
np.interp(power_curve_wind_speeds, [7.5, 12.5], [1 / 3, 2 / 3])
298+
),
299+
power_curve_values,
300+
left=0,
301+
right=0,
302+
)
278303
return power_output

0 commit comments

Comments
 (0)