Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
70e0156
changed default types to float in solarposition.py
Oct 29, 2024
0ec0cbf
switched to floats in the docstrings and removed type hints
Oct 30, 2024
5eb9d00
added magnus_tetens equations
Oct 30, 2024
ab2de3a
line too long in solarposition.py
Oct 30, 2024
f62bd8e
revert solarposition.py to pre-PR state
Oct 30, 2024
77025c7
set default magnus coefficients per conversation in #1744
Nov 5, 2024
b52f9a9
moved equations to atmosphere.py, updated function names to suggested…
Nov 11, 2024
58b246e
moved tests to atmosphere.py tests
Nov 11, 2024
a06871a
changed reference to WMO
Nov 11, 2024
caa4417
dry-bult temperature in the docstring
Nov 11, 2024
edd84b1
revert pyproject.toml
Nov 11, 2024
9c32f6a
remove uv.lock
Nov 11, 2024
63ed5c7
Update pvlib/atmosphere.py
kurt-rhee Nov 18, 2024
6312f92
fixing flake8 errors for tdew/rh functions
Nov 18, 2024
410e95f
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
d94df47
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
e6b5908
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
63a9226
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
459d5a6
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
05b846f
Update pvlib/atmosphere.py
kurt-rhee Nov 25, 2024
0ae31e1
added some unit tests for different coefficients and input types
kurt-rhee Dec 1, 2024
337f723
refactored tests, removed magnus_tetens, updated whatsnew
kurt-rhee Dec 5, 2024
8bc9b86
reference and whatsnew
kurt-rhee Dec 5, 2024
9651c3b
revert line 36 (linter)
kurt-rhee Dec 5, 2024
b860ca5
Update pvlib/atmosphere.py
kurt-rhee Dec 7, 2024
2bdc1dc
Update docs/sphinx/source/whatsnew/v0.11.2.rst
kurt-rhee Dec 7, 2024
c39c449
Update pvlib/atmosphere.py
kurt-rhee Dec 7, 2024
b5a6f09
Update pvlib/tests/test_atmosphere.py
kurt-rhee Dec 7, 2024
aedc835
Update pyproject.toml
kurt-rhee Dec 7, 2024
4fcb9e3
Merge branch 'main' into relative-humidity
AdamRJensen Dec 9, 2024
c956c91
Update pvlib/atmosphere.py
kurt-rhee Dec 10, 2024
f682815
Update pvlib/atmosphere.py
kurt-rhee Dec 10, 2024
0bfc6cb
Update pvlib/atmosphere.py
kurt-rhee Dec 10, 2024
71a165e
Update pvlib/atmosphere.py
kurt-rhee Dec 10, 2024
25527d6
Update pvlib/atmosphere.py
kurt-rhee Dec 10, 2024
1eff53f
added a round-trip test for magnus tetens
kurt-rhee Dec 11, 2024
df33a31
temperature -> temp_air; dewpoint -> temp_dew
kandersolar Dec 12, 2024
b4802a3
miscellaneous other cleanup
kandersolar Dec 12, 2024
3893692
tests: put assertions next to calculations
kandersolar Dec 12, 2024
11d81c3
linter
kandersolar Dec 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions pvlib/atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,82 @@
return pw


def rh_from_tdew(temperature, dewpoint, coeff=(6.112, 17.62, 243.12)):
Copy link
Member

@kandersolar kandersolar Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed earlier but forgot to comment: we should rename this parameter to temp_dew, the name used by other pvlib models and iotools functions. I'll push a commit for that to give @kurt-rhee a break from this PR :P

edit: and same for temp_air of course

"""
Calculate relative humidity from dewpoint temperatureusing Magnus equation.
This function was used by First Solar in creating their spectral model
and is therefore relevant to the first solar spectral model in pvlib.
Default magnus equation coefficients are from [2].
Parameters
----------
temperature : numeric
Air temperature (dry-bulb temperature) in degrees Celsius
dewpoint : numeric
Dewpoint temperature in degrees Celsius
coeff: tuple
Magnus equation coefficient (A, B, C)
Returns
-------
pd.Series
Relative humidity as percentage (0.0-100.0)
References
----------
.. [1] https://library.wmo.int/viewer/68695/?offset=3#page=220&viewer=picture&o=bookmark&n=0&q=

Check failure on line 363 in pvlib/atmosphere.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (99 > 79 characters)
.. [2] https://www.schweizerbart.de//papers/metz/detail/3/89544/Advancements_in_the_field_of_hygrometry?af=crossref

Check failure on line 364 in pvlib/atmosphere.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (119 > 79 characters)
"""

# Calculate vapor pressure (e) and saturation vapor pressure (es)
e = coeff[0] * np.exp((coeff[1] * temperature) / (coeff[2] + temperature))
es = coeff[0] * np.exp((coeff[1] * dewpoint) / (coeff[2] + dewpoint))

# Calculate relative humidity as percentage
relative_humidity = 100 * (es / e)

return relative_humidity


def tdew_from_rh(
temperature, relative_humidity, coeff=(6.112, 17.62, 243.12)
):
"""
Calculate dewpoint temperature using Magnus equation.
This is just a reversal of the calculation in calculate_relative_humidity.
Parameters
----------
temperature : numeric
Air temperature (dry-bulb temperature) in degrees Celsius
relative_humidity : numeric
Relative humidity as percentage (0-100)
Returns
-------
pd.Series
Dewpoint temperature in degrees Celsius
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Dewpoint temperature in degrees Celsius
Dew-point temperature in degrees Celsius

References
----------
.. [1] https://library.wmo.int/viewer/68695/?offset=3#page=220&viewer=picture&o=bookmark&n=0&q=

Check failure on line 398 in pvlib/atmosphere.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (99 > 79 characters)
"""
# Calculate the term inside the log
# From RH = 100 * (es/e), we get es = (RH/100) * e
# Substituting the Magnus equation and solving for dewpoint

# First calculate ln(es/A)
ln_term = (
(coeff[1] * temperature) / (coeff[2] + temperature)
+ np.log(relative_humidity/100)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like RH zero might throw an error or warning here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a check for zero values or leave the original message in the stack trace?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's necessary, RH=0% is practically impossible (source).

)

# Then solve for dewpoint
dewpoint = coeff[2] * ln_term / (coeff[1] - ln_term)

return dewpoint


first_solar_spectral_correction = deprecated(
since='0.10.0',
alternative='pvlib.spectrum.spectral_factor_firstsolar'
Expand Down
108 changes: 108 additions & 0 deletions pvlib/spectrum/magnus_tetens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import numpy as np


def magnus_tetens_aekr(temperature, dewpoint, A=6.112, B=17.62, C=243.12):
"""
Calculate relative humidity using Magnus equation with AEKR coefficients.
This function was used by First Solar in creating their spectral model
and is therefore relevant to the first solar spectral model in pvlib.
Default magnus equation coefficients are from [2].
Parameters
----------
temperature : pd.Series
Air temperature in degrees Celsius
dewpoint : pd.Series
Dewpoint temperature in degrees Celsius
A: float
Magnus equation coefficient A
B: float
Magnus equation coefficient B
C: float
Magnus equation coefficient C
Returns
-------
pd.Series
Relative humidity as percentage (0-100)
Notes
-----
Uses the AEKR coefficients which minimize errors between -40 and
50 degrees C according to reference [1].
References
----------
.. [1] https://www.osti.gov/servlets/purl/548871-PjpxAP/webviewable/
.. [2] https://www.schweizerbart.de//papers/metz/detail/3/89544/Advancements_in_the_field_of_hygrometry?af=crossref

Check failure on line 37 in pvlib/spectrum/magnus_tetens.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (119 > 79 characters)
"""

# Calculate vapor pressure (e) and saturation vapor pressure (es)
e = A * np.exp((B * temperature) / (C + temperature))
es = A * np.exp((B * dewpoint) / (C + dewpoint))

# Calculate relative humidity as percentage
relative_humidity = 100 * (es / e)

return relative_humidity


def reverse_magnus_tetens_aekr(
temperature, relative_humidity, B=17.62, C=243.12
):
"""
Calculate dewpoint temperature using Magnus equation with
AEKR coefficients. This is just a reversal of the calculation
in calculate_relative_humidity.
Parameters
----------
temperature : pd.Series
Air temperature in degrees Celsius
relative_humidity : pd.Series
Relative humidity as percentage (0-100)
Returns
-------
pd.Series
Dewpoint temperature in degrees Celsius
Notes
-----
Derived by solving the Magnus equation for dewpoint given
relative humidity.
Valid for temperatures between -40 and 50 degrees C.
References
----------
.. [1] https://www.osti.gov/servlets/purl/548871-PjpxAP/webviewable/
"""
# Calculate the term inside the log
# From RH = 100 * (es/e), we get es = (RH/100) * e
# Substituting the Magnus equation and solving for dewpoint

# First calculate ln(es/A)
ln_term = (
(B * temperature) / (C + temperature)
+ np.log(relative_humidity/100)
)

# Then solve for dewpoint
dewpoint = C * ln_term / (B - ln_term)

return dewpoint


if __name__ == "__main__":
import pandas as pd
rh = magnus_tetens_aekr(
temperature=pd.Series([20.0, 25.0, 30.0, 15.0, 10.0]),
dewpoint=pd.Series([15.0, 20.0, 25.0, 12.0, 8.0])
)

dewpoint = reverse_magnus_tetens_aekr(
temperature=pd.Series([20.0, 25.0, 30.0, 15.0, 10.0]),
relative_humidity=rh
)
print(rh)
print(dewpoint)
19 changes: 19 additions & 0 deletions pvlib/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,3 +474,22 @@
'IXXO': 3.18803,
'FD': 1}
return parameters


@pytest.fixture(scope='function')
def tdew_rh_conversion_temperature():
temperature = pd.Series([20.0, 25.0, 30.0, 15.0, 10.0])
return temperature


@pytest.fixture(scope='function')
def tdew_rh_conversion_dewpoint():
dewpoint = pd.Series([15.0, 20.0, 25.0, 12.0, 8.0])
return dewpoint

@pytest.fixture(scope='function')

Check failure on line 490 in pvlib/tests/conftest.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E302 expected 2 blank lines, found 1
def tdew_rh_conversion_relative_humidity():
relative_humidity = pd.Series([
72.938767, 73.802512, 74.628205, 82.261353, 87.383237
])
return relative_humidity
37 changes: 37 additions & 0 deletions pvlib/tests/test_atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@

assert_allclose(pws, expected, atol=0.01)

# Unit tests
def test_rh_from_tdew(

Check failure on line 91 in pvlib/tests/test_atmosphere.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E302 expected 2 blank lines, found 1
tdew_rh_conversion_temperature, tdew_rh_conversion_dewpoint,
tdew_rh_conversion_relative_humidity
):

# Calculate relative humidity
rh = atmosphere.rh_from_tdew(
temperature=tdew_rh_conversion_temperature,
dewpoint=tdew_rh_conversion_dewpoint
)

# test
pd.testing.assert_series_equal(
rh,
tdew_rh_conversion_relative_humidity,
check_names=False
)


# Unit tests
def test_tdew_from_rh(
tdew_rh_conversion_temperature, tdew_rh_conversion_dewpoint,
tdew_rh_conversion_relative_humidity
):

# Calculate relative humidity
dewpoint = atmosphere.tdew_from_rh(
temperature=tdew_rh_conversion_temperature,
relative_humidity=tdew_rh_conversion_relative_humidity
)

# test
pd.testing.assert_series_equal(
dewpoint, spectrum_dewpoint, check_names=False

Check failure on line 124 in pvlib/tests/test_atmosphere.py

View workflow job for this annotation

GitHub Actions / flake8-linter

F821 undefined name 'spectrum_dewpoint'
)


def test_first_solar_spectral_correction_deprecated():
with pytest.warns(pvlibDeprecationWarning,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ optional = [
]
doc = [
'ipython',
'pickleshare', # required by ipython
'pickleshare', # required by ipython
'matplotlib',
'sphinx == 7.3.7',
'pydata-sphinx-theme == 0.15.4',
Expand Down
Loading