-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Function to estimate wind speed at different heights #2124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
46f7804
wind_speed_at_different_heights
IoannisSifnaios a15b63e
minor fixed
IoannisSifnaios b8450d0
fixed toc and added test
IoannisSifnaios 91c1de3
edit math mode
IoannisSifnaios b611487
changed link for reference
IoannisSifnaios 663865d
Apply suggestions from code review
IoannisSifnaios f165f4d
Update pvlib/tests/test_windspeed.py
IoannisSifnaios 0cfb539
changed function name
IoannisSifnaios 1bd9308
Update pvlib/windspeed.py
IoannisSifnaios 9fef611
change measured height to reference
IoannisSifnaios 773ea6b
Merge branch 'wind_speed' of https://github.com/IoannisSifnaios/pvlib…
IoannisSifnaios 0a891a2
moved wind speed to atmosphere
IoannisSifnaios 1927357
minor formatting fixes
IoannisSifnaios cd51ad3
update test parameter names
IoannisSifnaios 6d33076
updated error message
IoannisSifnaios e3bd1f8
fixed error message vol.2
IoannisSifnaios 3850388
updated tests
IoannisSifnaios 0cfbd79
Update v0.11.1.rst
IoannisSifnaios aca975c
Merge branch 'main' into wind_speed
IoannisSifnaios 07be0b7
changed nan condition
IoannisSifnaios 2133f0c
changed function name
IoannisSifnaios 488fb99
Apply suggestions from code review
IoannisSifnaios 251362c
changed name (again)
IoannisSifnaios dbee472
Merge branch 'wind_speed' of https://github.com/IoannisSifnaios/pvlib…
IoannisSifnaios 0809425
condition fix
IoannisSifnaios 5cefc11
corrected the Sandia wording
IoannisSifnaios File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,3 +25,4 @@ wrap the functions listed below. See its documentation for details. | |
| system_models | ||
| parameters | ||
| other | ||
| windspeed | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| .. currentmodule:: pvlib | ||
|
|
||
| Wind Speed | ||
| ---------- | ||
|
|
||
| .. autosummary:: | ||
| :toctree: ../generated/ | ||
|
|
||
| windspeed.wind_speed_at_height_hellmann |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,4 +27,5 @@ | |
| temperature, | ||
| tools, | ||
| tracking, | ||
| windspeed, | ||
| ) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| import numpy as np | ||
| import pandas as pd | ||
| import pytest | ||
| from pvlib import windspeed | ||
|
|
||
| from .conftest import assert_series_equal | ||
| from numpy.testing import assert_allclose | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| 'wind_speed_measured,height_measured,height_desired,wind_speed_calc', | ||
| [ | ||
| (10, -2, 5, np.nan), | ||
| (-10, 2, 5, np.nan), | ||
| (5, 4, 5, 5.067393209486324), | ||
| (7, 6, 10, 7.2178684911195905), | ||
| (10, 8, 20, 10.565167835216586), | ||
| (12, 10, 30, 12.817653329393977)]) | ||
| def test_wind_speed_at_height_hellmann(wind_speed_measured, | ||
| height_measured, | ||
| height_desired, | ||
| wind_speed_calc): | ||
| result = windspeed.wind_speed_at_height_hellmann( | ||
| wind_speed_measured, | ||
| height_measured, | ||
| height_desired, | ||
| surface_type='unstable_air_above_open_water_surface') | ||
| assert_allclose(result, wind_speed_calc) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def times(): | ||
| return pd.date_range(start="2015-01-01 00:00", end="2015-01-01 05:00", | ||
| freq="1h") | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def wind_speeds_measured(times): | ||
| return pd.Series([10, -10, 5, 7, 10, 12], index=times) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def heights_measured(times): | ||
| return np.array([-2, 2, 4, 6, 8, 10]) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def heights_desired(): | ||
| return np.array([5, 5, 5, 10, 20, 30]) | ||
|
|
||
|
|
||
| @pytest.fixture | ||
| def wind_speeds_calc(times): | ||
| return pd.Series([np.nan, np.nan, 5.067393209486324, 7.2178684911195905, | ||
| 10.565167835216586, 12.817653329393977], index=times) | ||
|
|
||
|
|
||
| def test_wind_speed_at_height_hellmann_ndarray(wind_speeds_measured, | ||
| heights_measured, | ||
| heights_desired, | ||
| wind_speeds_calc): | ||
| # test wind speed estimation by passing in surface_type | ||
| result_surface = windspeed.wind_speed_at_height_hellmann( | ||
| wind_speeds_measured.to_numpy(), | ||
| heights_measured, | ||
| heights_desired, | ||
| surface_type='unstable_air_above_open_water_surface') | ||
| assert_allclose(wind_speeds_calc.to_numpy(), result_surface) | ||
| # test wind speed estimation by passing in the exponent corresponding | ||
| # to the surface_type above | ||
| result_exponent = windspeed.wind_speed_at_height_hellmann( | ||
| wind_speeds_measured.to_numpy(), | ||
| heights_measured, | ||
| heights_desired, | ||
| exponent=0.06) | ||
| assert_allclose(wind_speeds_calc.to_numpy(), result_exponent) | ||
|
|
||
|
|
||
| def test_wind_speed_at_height_hellmann_series(wind_speeds_measured, | ||
| heights_measured, | ||
| heights_desired, | ||
| wind_speeds_calc): | ||
| result = windspeed.wind_speed_at_height_hellmann( | ||
| wind_speeds_measured, | ||
| heights_measured, | ||
| heights_desired, | ||
| surface_type='unstable_air_above_open_water_surface') | ||
| assert_series_equal(wind_speeds_calc, result) | ||
|
|
||
|
|
||
| def test_wind_speed_at_height_hellmann_invalid(): | ||
| with pytest.raises(ValueError, match='Either a `surface_type` has to be ' | ||
| 'chosen or an exponent'): | ||
| # no exponent or surface_type given | ||
| windspeed.wind_speed_at_height_hellmann(wind_speed_measured=10, | ||
| height_measured=5, | ||
| height_desired=10) | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| with pytest.raises(KeyError, match='not_an_exponent'): | ||
| # invalid surface_type | ||
| windspeed.wind_speed_at_height_hellmann(wind_speed_measured=10, | ||
| height_measured=5, | ||
| height_desired=10, | ||
| surface_type='not_an_exponent') | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| """The ``windspeed`` module contains functions for calculating wind speed.""" | ||
|
|
||
| import numpy as np | ||
| import pandas as pd | ||
|
|
||
|
|
||
| # Values of the Hellmann exponent | ||
| HELLMANN_SURFACE_EXPONENTS = { | ||
| 'unstable_air_above_open_water_surface': 0.06, | ||
| 'neutral_air_above_open_water_surface': 0.10, | ||
| 'stable_air_above_open_water_surface': 0.27, | ||
| 'unstable_air_above_flat_open_coast': 0.11, | ||
| 'neutral_air_above_flat_open_coast': 0.16, | ||
| 'stable_air_above_flat_open_coast': 0.40, | ||
| 'unstable_air_above_human_inhabited_areas': 0.27, | ||
| 'neutral_air_above_human_inhabited_areas': 0.34, | ||
| 'stable_air_above_human_inhabited_areas': 0.60, | ||
| } | ||
|
|
||
|
|
||
| def wind_speed_at_height_hellmann(wind_speed_measured, height_measured, | ||
| height_desired, exponent=None, | ||
| surface_type=None): | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| r""" | ||
| Estimate wind speed for different heights. | ||
|
|
||
| The model is based on the power law equation by Hellmann [1]_, [2]_. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| wind_speed_measured : numeric | ||
| Measured wind speed. [m/s] | ||
|
|
||
| height_measured : float | ||
| The height at which the wind speed is measured. [m] | ||
|
|
||
| height_desired : float | ||
| The height at which the wind speed will be estimated. [m] | ||
|
|
||
| exponent : float, optional | ||
| Exponent based on the surface type. [-] | ||
|
|
||
| surface_type : string, optional | ||
| If supplied, overrides ``exponent``. Can be one of the following | ||
| (see [1]_): | ||
|
|
||
| * ``'unstable_air_above_open_water_surface'`` | ||
| * ``'neutral_air_above_open_water_surface'`` | ||
| * ``'stable_air_above_open_water_surface'`` | ||
| * ``'unstable_air_above_flat_open_coast'`` | ||
| * ``'neutral_air_above_flat_open_coast'`` | ||
| * ``'stable_air_above_flat_open_coast'`` | ||
| * ``'unstable_air_above_human_inhabited_areas'`` | ||
| * ``'neutral_air_above_human_inhabited_areas'`` | ||
| * ``'stable_air_above_human_inhabited_areas'`` | ||
|
|
||
| Returns | ||
| ------- | ||
| wind_speed : numeric | ||
| Adjusted wind speed for the desired height. [m/s] | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If neither of ``exponent`` nor a ``surface_type`` is given. | ||
| If both ``exponent`` nor a ``surface_type`` is given. These parameters | ||
| are mutually exclusive. | ||
|
|
||
| KeyError | ||
| If the specified ``surface_type`` is invalid. | ||
|
|
||
| Notes | ||
| ----- | ||
| The equation for calculating the wind speed at a height of :math:`h` is | ||
| given by the following power law equation [1]_, [2]_: | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
|
|
||
| .. math:: | ||
| :label: wind speed | ||
|
|
||
| windspeed_h = windspeed_{href} \cdot \left( \frac{h}{h_{ref}} \right)^a | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
|
|
||
| where :math:`h` [m] is the height at which we would like to calculate the | ||
| wind speed, :math:`h_{ref}` [m] is the reference height at which the wind | ||
| speed is known, and :math:`windspeed_h` [m/s] and :math:`windspeed_{href}` | ||
| [m/s] are the corresponding wind speeds at these heights. :math:`a` is a | ||
| value that depends on the surface type. Some values found in the literature | ||
| [1]_ for :math:`a` are the following: | ||
|
|
||
| .. table:: Values for the Hellmann-exponent | ||
|
|
||
| +-----------+--------------------+------------------+------------------+ | ||
| | Stability | Open water surface | Flat, open coast | Cities, villages | | ||
| +===========+====================+==================+==================+ | ||
| | Unstable | 0.06 | 0.10 | 0.27 | | ||
| +-----------+--------------------+------------------+------------------+ | ||
| | Neutral | 0.11 | 0.16 | 0.40 | | ||
| +-----------+--------------------+------------------+------------------+ | ||
| | Stable | 0.27 | 0.34 | 0.60 | | ||
| +-----------+--------------------+------------------+------------------+ | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
|
|
||
| In a report by SANDIA [3]_, this equation was experimentally tested for a | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| height of 30 ft (9.144 m) and the following coefficients were recommended: | ||
| :math:`h_{ref} = 9.144` [m], :math:`a = 0.219` [-], and | ||
| :math:`windspeed_{href}` is the wind speed at a height of 9.144 [m]. | ||
|
|
||
| It should be noted that the equation retuns a value of NaN if the | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| calculated wind speed is negative or a complex number. | ||
|
|
||
| Warning | ||
| ------- | ||
| Module temperature functions often require wind speeds at a heigt of 10 m | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| and not the wind speed at the module height. | ||
|
|
||
| For example, the following temperature functions require the input wind | ||
| speed to be 10 m: :py:func:`~pvlib.temperature.sapm_cell`, | ||
| :py:func:`~pvlib.temperature.sapm_module`, and | ||
| :py:func:`~pvlib.temperature.generic_linear`, whereas the | ||
| :py:func:`~pvlib.temperature.fuentes` model requires wind speed at 9.144 m. | ||
|
|
||
| Additionally, the heat loss coefficients of some models have been developed | ||
| for wind speed measurements at 10 m (e.g., | ||
| :py:func:`~pvlib.temperature.pvsyst_cell`, | ||
| :py:func:`~pvlib.temperature.faiman`, and | ||
| :py:func:`~pvlib.temperature.faiman_rad`). | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
|
|
||
| References | ||
| ---------- | ||
| .. [1] Kaltschmitt M., Streicher W., Wiese A. (2007). "Renewable Energy: | ||
| Technology, Economics and Environment." Springer, | ||
| :doi:`10.1007/3-540-70949-5`. | ||
|
|
||
| .. [2] Hellmann G. (1915). "Über die Bewegung der Luft in den untersten | ||
| Schichten der Atmosphäre." Meteorologische Zeitschrift, 32 | ||
|
|
||
| .. [3] Menicucci D.F., Hall I.J. (1985). "Estimating wind speed as a | ||
| function of height above ground: An analysis of data obtained at the | ||
| southwest residential experiment station, Las Cruses, New Mexico." | ||
| SAND84-2530, Sandia National Laboratories. | ||
| `source <https://web.archive.org/web/20230418202422/https://www2.jpl.nasa.gov/adv_tech/photovol/2016CTR/SNL%20-%20Est%20Wind%20Speed%20vs%20Height_1985.pdf>`_ # noqa:E501 | ||
| """ | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| if surface_type is not None and exponent is None: | ||
| # use the Hellmann exponent from dictionary | ||
| exponent = HELLMANN_SURFACE_EXPONENTS[surface_type] | ||
| elif surface_type is None and exponent is not None: | ||
| # use the provided exponent | ||
| pass | ||
| else: | ||
| raise ValueError( | ||
| "Either a `surface_type` has to be chosen or an exponent") | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
|
|
||
| wind_speed = wind_speed_measured * ( | ||
| (height_desired / height_measured) ** exponent) | ||
|
|
||
| # if the provided height is negative the calculated wind speed is complex | ||
| # so a NaN value is returned | ||
| if isinstance(wind_speed, complex): | ||
| wind_speed = np.nan | ||
|
|
||
| # if wind speed is nagative return NaN | ||
|
IoannisSifnaios marked this conversation as resolved.
Outdated
|
||
| wind_speed = np.where(wind_speed < 0, np.nan, wind_speed) | ||
|
|
||
| if isinstance(wind_speed_measured, pd.Series): | ||
| wind_speed = pd.Series(wind_speed, index=wind_speed_measured.index) | ||
|
|
||
| return wind_speed | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.