Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions docs/sphinx/source/whatsnew/v0.15.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ Documentation
* Clarify how Linke turbidity values can be provided to
:py:func:`pvlib.clearsky.ineichen` via
:py:func:`pvlib.clearsky.lookup_linke_turbidity`. (:issue:`2598`, :pull:`2746`)
* Clarifies how Linke turbidity values can be provided to
:py:func:`pvlib.clearsky.ineichen` via
:py:func:`pvlib.clearsky.lookup_linke_turbidity` (:issue:`2598`, :pull:`2746`)
* Clarifies the variable naming, comments and references in
:py:func:`pvlib.atmosphere.rh_from_tdew` (and the related comments in
:py:func:`pvlib.atmosphere.tdew_from_rh`) so the actual and saturation vapor
pressures are no longer swapped in the source. The returned values are
Comment on lines +61 to +62

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
:py:func:`pvlib.atmosphere.tdew_from_rh`) so the actual and saturation vapor
pressures are no longer swapped in the source. The returned values are
:py:func:`pvlib.atmosphere.tdew_from_rh`) related to the actual
and saturation vapor pressures. Function output is

unchanged. (:issue:`2734`, :pull:`2782`)


Testing
Expand Down
38 changes: 25 additions & 13 deletions pvlib/atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,19 +363,34 @@ def rh_from_tdew(temp_air, temp_dew, coeff=(6.112, 17.62, 243.12)):
numeric
Relative humidity (0.0-100.0). [%]

Notes
-----
Relative humidity is computed as ``100 * e / es``, where the actual vapor
pressure ``e`` is the saturation vapor pressure at the dew point and the
Comment on lines +368 to +369

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Relative humidity is computed as ``100 * e / es``, where the actual vapor
pressure ``e`` is the saturation vapor pressure at the dew point and the
Relative humidity is computed as ``100 * e / es``, where ``e`` is the
saturation vapor pressure at the dew point temperature, and ``es``

saturation vapor pressure ``es`` is evaluated at the air temperature, both
from the Magnus equation ``A * exp(B * T / (C + T))``. The default
Comment on lines +370 to +371

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
saturation vapor pressure ``es`` is evaluated at the air temperature, both
from the Magnus equation ``A * exp(B * T / (C + T))``. The default
is the saturation vapor pressureat the air temperature, both
from the Magnus equation ``A * exp(B * T / (C + T))``. The default

coefficients ``(A, B, C) = (6.112, 17.62, 243.12)`` are the WMO-recommended
Magnus form for saturation over liquid water, valid for temperatures from
-45 to +60 °C [1]_; see [2]_ for the approximation and its accuracy.

References
----------
.. [1] "Guide to Instruments and Methods of Observation",
World Meteorological Organization, WMO-No. 8, 2023.
https://library.wmo.int/idurl/4/68695
.. [2] O. A. Alduchov and R. E. Eskridge, "Improved Magnus Form
Approximation of Saturation Vapor Pressure", Journal of Applied
Meteorology, 35(4), pp. 601-609, 1996.
"""

# Calculate vapor pressure (e) and saturation vapor pressure (es)
e = coeff[0] * np.exp((coeff[1] * temp_air) / (coeff[2] + temp_air))
es = coeff[0] * np.exp((coeff[1] * temp_dew) / (coeff[2] + temp_dew))
# Actual vapor pressure ``e`` is the saturation vapor pressure at the dew
# point; the saturation vapor pressure ``es`` is taken at the air
# temperature. Both come from the Magnus equation.
e = coeff[0] * np.exp((coeff[1] * temp_dew) / (coeff[2] + temp_dew))
es = coeff[0] * np.exp((coeff[1] * temp_air) / (coeff[2] + temp_air))

# Calculate relative humidity as percentage
relative_humidity = 100 * (es / e)
# Relative humidity is their ratio, as a percentage.
relative_humidity = 100 * (e / es)

return relative_humidity

Expand Down Expand Up @@ -406,17 +421,14 @@ def tdew_from_rh(temp_air, relative_humidity, coeff=(6.112, 17.62, 243.12)):
World Meteorological Organization, WMO-No. 8, 2023.
https://library.wmo.int/idurl/4/68695
"""
# 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)
# Invert RH = 100 * (e / es): the actual vapor pressure is
# e = (RH / 100) * es, and the dew point is the temperature at which the
# saturation vapor pressure equals e. Substituting the Magnus equation for
# both and solving for the dew point gives the expression below.
ln_term = (
(coeff[1] * temp_air) / (coeff[2] + temp_air)
+ np.log(relative_humidity/100)
+ np.log(relative_humidity / 100)
)

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

return dewpoint
Expand Down
13 changes: 13 additions & 0 deletions tests/test_atmosphere.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,19 @@ def test_rh_from_tdew():
assert np.isclose(rh_float, relative_humidity_wmo.iloc[0])


def test_rh_from_tdew_physical_bounds():
# The dew point cannot exceed the air temperature: equal values mean the
# air is saturated (100% RH), and a lower dew point gives a lower RH. This
# pins the direction of the calculation so it cannot silently invert.
assert atmosphere.rh_from_tdew(
temp_air=20.0, temp_dew=20.0
) == pytest.approx(100.0)
assert atmosphere.rh_from_tdew(temp_air=20.0, temp_dew=10.0) < 100.0
assert atmosphere.rh_from_tdew(
temp_air=20.0, temp_dew=5.0
) < atmosphere.rh_from_tdew(temp_air=20.0, temp_dew=15.0)


# Unit tests
def test_tdew_from_rh():

Expand Down
Loading