|
10 | 10 | import numpy as np |
11 | 11 | import pandas as pd |
12 | 12 | from windpowerlib import tools |
13 | | -from matplotlib import pyplot as plt |
14 | | -import os |
15 | 13 |
|
16 | 14 |
|
17 | 15 | def power_coefficient_curve(wind_speed, power_coefficient_curve_wind_speeds, |
@@ -245,305 +243,3 @@ def power_curve_density_correction(wind_speed, power_curve_wind_speeds, |
245 | 243 | else: |
246 | 244 | power_output = np.array(power_output) |
247 | 245 | return power_output |
248 | | - |
249 | | - |
250 | | -def smooth_power_curve(power_curve_wind_speeds, power_curve_values, |
251 | | - block_width=0.5, |
252 | | - standard_deviation_method='turbulence_intensity', |
253 | | - mean_gauss=0, **kwargs): |
254 | | - r""" |
255 | | - Smoothes the input power curve values by using a gaussian distribution. |
256 | | -
|
257 | | - Parameters |
258 | | - ---------- |
259 | | - power_curve_wind_speeds : pandas.Series |
260 | | - Wind speeds in m/s for which the power curve values are provided in |
261 | | - `power_curve_values`. |
262 | | - power_curve_values : pandas.Series or numpy.array |
263 | | - Power curve values corresponding to wind speeds in |
264 | | - `power_curve_wind_speeds`. |
265 | | - block_width : Float |
266 | | - Width of the moving block. Default: 0.5. |
267 | | - standard_deviation_method : String |
268 | | - Method for calculating the standard deviation for the gaussian |
269 | | - distribution. Options: 'turbulence_intensity', 'Norgaard', 'Staffell'. |
270 | | - Default: 'turbulence_intensity'. |
271 | | -
|
272 | | - Other Parameters |
273 | | - ---------------- |
274 | | - turbulence intensity : Float, optional |
275 | | - Turbulence intensity at hub height of the wind turbine the power curve |
276 | | - is smoothed for. |
277 | | -
|
278 | | - Returns |
279 | | - ------- |
280 | | - smoothed_power_curve_df : pd.DataFrame |
281 | | - Smoothed power curve. DataFrame has 'wind_speed' and |
282 | | - 'power' columns with wind speeds in m/s and the corresponding power |
283 | | - curve value in W. |
284 | | -
|
285 | | - Notes |
286 | | - ----- |
287 | | - The following equation is used [1]_: |
288 | | - # TODO: add equations |
289 | | -
|
290 | | - References |
291 | | - ---------- |
292 | | - .. [1] Knorr, K.: "Modellierung von raum-zeitlichen Eigenschaften der |
293 | | - Windenergieeinspeisung für wetterdatenbasierte |
294 | | - Windleistungssimulationen". Universität Kassel, Diss., 2016, |
295 | | - p. 106 |
296 | | -
|
297 | | - # TODO: add references |
298 | | - """ |
299 | | - # Specify normalized standard deviation |
300 | | - if standard_deviation_method == 'turbulence_intensity': |
301 | | - if 'turbulence_intensity' in kwargs: |
302 | | - normalized_standard_deviation = kwargs['turbulence_intensity'] |
303 | | - else: |
304 | | - raise ValueError("Turbulence intensity must be defined for " + |
305 | | - "using 'turbulence_intensity' as " + |
306 | | - "`standard_deviation_method`") |
307 | | - elif standard_deviation_method == 'Norgaard': |
308 | | - pass # TODO add |
309 | | - elif standard_deviation_method == 'Staffell': |
310 | | - normalized_standard_deviation = 0.2 |
311 | | - # Initialize list for power curve values |
312 | | - smoothed_power_curve_values = [] |
313 | | - # Step of power curve wind speeds |
314 | | - step = power_curve_wind_speeds.iloc[-5] - power_curve_wind_speeds.iloc[-6] |
315 | | - # Append wind speeds to `power_curve_wind_speeds` until 40 m/s |
316 | | - while (power_curve_wind_speeds.values[-1] < 40.0): |
317 | | - power_curve_wind_speeds = power_curve_wind_speeds.append( |
318 | | - pd.Series(power_curve_wind_speeds.iloc[-1] + step, |
319 | | - index=[power_curve_wind_speeds.index[-1] + 1])) |
320 | | - power_curve_values = power_curve_values.append( |
321 | | - pd.Series(0.0, index=[power_curve_values.index[-1] + 1])) |
322 | | - for power_curve_wind_speed in power_curve_wind_speeds: |
323 | | - # Create array of wind speeds for the moving block |
324 | | - wind_speeds_block = ( |
325 | | - np.arange(-15.0, 15.0 + block_width, block_width) + |
326 | | - power_curve_wind_speed) |
327 | | - # Get standard deviation for gaussian filter |
328 | | - standard_deviation = ( |
329 | | - (power_curve_wind_speed * normalized_standard_deviation + 0.6) |
330 | | - if standard_deviation_method is 'Staffell' |
331 | | - else power_curve_wind_speed * normalized_standard_deviation) |
332 | | - # Get the smoothed value of the power output |
333 | | - smoothed_value = sum( |
334 | | - block_width * np.interp(wind_speed, power_curve_wind_speeds, |
335 | | - power_curve_values, left=0, right=0) * |
336 | | - tools.gaussian_distribution( |
337 | | - power_curve_wind_speed - wind_speed, |
338 | | - standard_deviation, mean_gauss) |
339 | | - for wind_speed in wind_speeds_block) |
340 | | - # Add value to list - add 0 if `smoothed_value` is nan. This occurs |
341 | | - # because the gaussian distribution is not defined for 0. |
342 | | - smoothed_power_curve_values.append(0 if np.isnan(smoothed_value) |
343 | | - else smoothed_value) |
344 | | - # Create smoothed power curve DataFrame |
345 | | - smoothed_power_curve_df = pd.DataFrame( |
346 | | - data=[list(power_curve_wind_speeds.values), |
347 | | - smoothed_power_curve_values]).transpose() |
348 | | - # Rename columns of DataFrame |
349 | | - smoothed_power_curve_df.columns = ['wind_speed', 'power'] |
350 | | -# # Plot power curves |
351 | | -# fig = plt.figure() |
352 | | -# plt.plot(power_curve_wind_speeds.values, power_curve_values.values) |
353 | | -# plt.plot(power_curve_wind_speeds.values, smoothed_power_curve_values) |
354 | | -# fig.savefig(os.path.abspath(os.path.join( |
355 | | -# os.path.dirname(__file__), '../Plots/power_curves', |
356 | | -# '{0}_{1}_{2}.png'.format(kwargs['object_name'], |
357 | | -# standard_deviation_method, block_width)))) |
358 | | -# plt.close() |
359 | | - return smoothed_power_curve_df |
360 | | - |
361 | | - |
362 | | -def wake_losses_to_power_curve(power_curve_wind_speeds, power_curve_values, |
363 | | - wake_losses_method='constant_efficiency', |
364 | | - wind_farm_efficiency=None): |
365 | | - r""" |
366 | | - Applies wake losses depending on the method to a power curve. |
367 | | -
|
368 | | - Parameters |
369 | | - ---------- |
370 | | - power_curve_wind_speeds : pandas.Series |
371 | | - Wind speeds in m/s for which the power curve values are provided in |
372 | | - `power_curve_values`. |
373 | | - power_curve_values : pandas.Series or numpy.array |
374 | | - Power curve values corresponding to wind speeds in |
375 | | - `power_curve_wind_speeds`. |
376 | | - wake_losses_method : String |
377 | | - Defines the method for talking wake losses within the farm into |
378 | | - consideration. Default: 'constant_efficiency'. |
379 | | - wind_farm_efficiency : Float or pd.DataFrame or Dictionary |
380 | | - Efficiency of the wind farm. Either constant (float) or wind efficiency |
381 | | - curve (pd.DataFrame or Dictionary) contianing 'wind_speed' and |
382 | | - 'efficiency' columns/keys with wind speeds in m/s and the |
383 | | - corresponding dimensionless wind farm efficiency. Default: None. |
384 | | -
|
385 | | - Returns |
386 | | - ------- |
387 | | - power_curve_df : pd.DataFrame |
388 | | - With wind farm efficiency reduced power curve. DataFrame power curve |
389 | | - values in W with the corresponding wind speeds in m/s. |
390 | | -
|
391 | | - Notes |
392 | | - ----- |
393 | | - TODO add |
394 | | -
|
395 | | - """ |
396 | | - # Create power curve DataFrame |
397 | | - power_curve_df = pd.DataFrame( |
398 | | - data=[list(power_curve_wind_speeds), |
399 | | - list(power_curve_values)]).transpose() |
400 | | - # Rename columns of DataFrame |
401 | | - power_curve_df.columns = ['wind_speed', 'power'] |
402 | | - if wake_losses_method == 'constant_efficiency': |
403 | | - if not isinstance(wind_farm_efficiency, float): |
404 | | - raise TypeError("'wind_farm_efficiency' must be float if " + |
405 | | - "`wake_losses_method´ is '{0}'") |
406 | | - power_curve_df['power'] = power_curve_values * wind_farm_efficiency |
407 | | - elif wake_losses_method == 'wind_efficiency_curve': |
408 | | - if (not isinstance(wind_farm_efficiency, dict) and |
409 | | - not isinstance(wind_farm_efficiency, pd.DataFrame)): |
410 | | - raise TypeError( |
411 | | - "'wind_farm_efficiency' must be a dictionary or " + |
412 | | - "pd.DataFrame if `wake_losses_method´ is '{0}'") |
413 | | - df = pd.concat([power_curve_df.set_index('wind_speed'), |
414 | | - wind_farm_efficiency.set_index('wind_speed')], axis=1) |
415 | | - # Add by efficiency reduced power column (nan values of efficiency |
416 | | - # are interpolated) |
417 | | - df['reduced_power'] = df['power'] * df['efficiency'].interpolate( |
418 | | - method='index') |
419 | | - reduced_power = df['reduced_power'].dropna() |
420 | | - power_curve_df = pd.DataFrame([reduced_power.index, |
421 | | - reduced_power.values]).transpose() |
422 | | - power_curve_df.columns = ['wind_speed', 'power'] |
423 | | - else: |
424 | | - raise ValueError( |
425 | | - "`wake_losses_method` is {0} but should be None, ".format( |
426 | | - wake_losses_method) + |
427 | | - "'constant_efficiency' or 'wind_efficiency_curve'") |
428 | | - return power_curve_df |
429 | | - |
430 | | - |
431 | | -def summarized_power_curve(wind_turbine_fleet, smoothing=True, |
432 | | - density_correction=False, wake_losses_method=None, |
433 | | - **kwargs): |
434 | | - r""" |
435 | | - Creates a summarized power curve for a wind turbine fleet. |
436 | | -
|
437 | | - Power curve is created by summing up all power curves. Depending on the |
438 | | - input paramters the power cuvers are smoothed before the summation and/or |
439 | | - a wind farm efficiency is applied after the summation. |
440 | | -
|
441 | | - Parameters |
442 | | - ---------- |
443 | | - wind_turbine_fleet : List of Dictionaries |
444 | | - Dictionaries with the keys 'wind_turbine' (contains |
445 | | - :class:`~.wind_turbine.WindTurbine` object) and 'number_of_turbines' |
446 | | - (contains number of turbine type in 'wind_turbine' key). |
447 | | - smoothing : Boolean |
448 | | - If True the power curves will be smoothed before the summation. |
449 | | - Default: True. |
450 | | - density_correction : Boolean |
451 | | - If True a density correction will be applied to the power curves |
452 | | - before the summation. Default: False. |
453 | | - wake_losses_method : String |
454 | | - Defines the method for talking wake losses within the farm into |
455 | | - consideration. Default: None. |
456 | | -
|
457 | | - Other Parameters |
458 | | - ---------------- |
459 | | - block_width : Float, optional |
460 | | - Width of the moving block. |
461 | | - Default in :py:func:`~.smooth_power_curve`: 0.5. |
462 | | - standard_deviation_method : String, optional |
463 | | - Method for calculating the standard deviation for the gaussian |
464 | | - distribution. Options: 'turbulence_intensity', 'Norgaard', 'Staffell'. |
465 | | - Default in :py:func:`~.smooth_power_curve`: 'turbulence_intensity'. |
466 | | - turbulence_intensity : Float, optional |
467 | | - Turbulence intensity at hub height of the wind turbine the power curve |
468 | | - is smoothed for. If this parameter is not given the turbulence |
469 | | - intensity is calculated via the `roughness_length`. |
470 | | - roughness_length : Float, optional |
471 | | - Roughness length. Only needed if `turbulence_intensity` is not given |
472 | | - and `standard_deviation_method` is 'turbulence_intensity' or not given. |
473 | | - wind_farm_efficiency : Float or pd.DataFrame or Dictionary, optional |
474 | | - Efficiency of the wind farm. Either constant (float) or wind efficiency |
475 | | - curve (pd.DataFrame or Dictionary) contianing 'wind_speed' and |
476 | | - 'efficiency' columns/keys with wind speeds in m/s and the |
477 | | - corresponding dimensionless wind farm efficiency. |
478 | | -
|
479 | | - Returns |
480 | | - ------- |
481 | | - summarized_power_curve_df : pd.DataFrame |
482 | | - Summarized power curve. DataFrame has 'wind_speed' and |
483 | | - 'power' columns with wind speeds in m/s and the corresponding power |
484 | | - curve value in W. |
485 | | -
|
486 | | - """ |
487 | | - # Initialize data frame for power curve values |
488 | | - df = pd.DataFrame() |
489 | | - for turbine_type_dict in wind_turbine_fleet: |
490 | | - # Start power curve |
491 | | - power_curve = pd.DataFrame( |
492 | | - turbine_type_dict['wind_turbine'].power_curve) |
493 | | - if smoothing: |
494 | | - if ('standard_deviation_method' not in kwargs or |
495 | | - kwargs['standard_deviation_method'] == |
496 | | - 'turbulence_intensity'): |
497 | | - if 'turbulence_intensity' not in kwargs: |
498 | | - if 'roughness_length' in kwargs: |
499 | | - # Calculate turbulence intensity and write to kwargs |
500 | | - turbulence_intensity = ( |
501 | | - tools.estimate_turbulence_intensity( |
502 | | - turbine_type_dict['wind_turbine'].hub_height, |
503 | | - kwargs['roughness_length'])) |
504 | | - kwargs['turbulence_intensity'] = turbulence_intensity |
505 | | - else: |
506 | | - raise ValueError( |
507 | | - "`roughness_length` must be defined for using" + |
508 | | - "'turbulence_intensity' as " + |
509 | | - "`standard_deviation_method`") |
510 | | - # Get smoothed power curve |
511 | | - power_curve = smooth_power_curve(power_curve['wind_speed'], |
512 | | - power_curve['power'], **kwargs) |
513 | | - if density_correction: |
514 | | - pass # TODO: add |
515 | | - # Add power curves of all turbines of same type to data frame after |
516 | | - # renaming columns |
517 | | - power_curve.columns = ['wind_speed', |
518 | | - turbine_type_dict['wind_turbine'].object_name] |
519 | | - df = pd.concat([df, pd.DataFrame( |
520 | | - power_curve.set_index(['wind_speed']) * |
521 | | - turbine_type_dict['number_of_turbines'])], axis=1) |
522 | | - # Rename back TODO: copy() |
523 | | - power_curve.columns = ['wind_speed', 'power'] |
524 | | - # Sum up power curves of all turbine types |
525 | | - summarized_power_curve = pd.DataFrame( |
526 | | - sum(df[item].interpolate(method='index') for item in list(df))) |
527 | | - summarized_power_curve.columns = ['power'] |
528 | | - # Take wake losses into consideration if `wake_losses_method` not None |
529 | | - if wake_losses_method is None: |
530 | | - # Create DataFrame of the above power curve data |
531 | | - summarized_power_curve_df = pd.DataFrame( |
532 | | - data=[list(summarized_power_curve.index), |
533 | | - list(summarized_power_curve['power'].values)]).transpose() |
534 | | - # Rename columns of DataFrame |
535 | | - summarized_power_curve_df.columns = ['wind_speed', 'power'] |
536 | | - elif (wake_losses_method == 'constant_efficiency' or |
537 | | - wake_losses_method == 'wind_efficiency_curve'): |
538 | | - try: |
539 | | - kwargs['wind_farm_efficiency'] |
540 | | - except KeyError: |
541 | | - raise KeyError("'wind_farm_efficiency' must be in kwargs when " + |
542 | | - "`wake_losses_method´ is '{0}'".format( |
543 | | - wake_losses_method)) |
544 | | - summarized_power_curve_df = wake_losses_to_power_curve( |
545 | | - summarized_power_curve.index, |
546 | | - summarized_power_curve['power'].values, |
547 | | - wake_losses_method=wake_losses_method, |
548 | | - wind_farm_efficiency=kwargs['wind_farm_efficiency']) |
549 | | - return summarized_power_curve_df |
0 commit comments