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
5 changes: 5 additions & 0 deletions docs/sphinx/source/whatsnew/v0.15.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ Documentation
* 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
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
saturation vapor pressure ``es`` is evaluated at 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 [1]_, valid for saturation over liquid water (see [2]_ for the
approximation and its temperature range).

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