Skip to content

Commit 8b7dc19

Browse files
committed
ENH: Avoid returning negative saturation mixing ratios
This corresponds to being outside the region of liquid phase water equilibrium, where e_s >= p. Detect this case, warn, and instead return NaN.
1 parent ca236e5 commit 8b7dc19

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

src/metpy/calc/thermo.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1748,11 +1748,24 @@ def saturation_mixing_ratio(total_press, temperature):
17481748
17491749
.. math:: r_s = \epsilon \frac{e_s}{p - e_s}
17501750
1751+
By definition, this value is only defined for conditions where the saturation vapor
1752+
pressure (:math:`e_s`) for the given temperature is less than the given total pressure
1753+
(:math:`p`). Otherwise, liquid phase water cannot exist in equilibrium and there is only
1754+
water vapor present. For any value pairs that fall under this condition, the function will
1755+
warn and return NaN.
1756+
17511757
.. versionchanged:: 1.0
17521758
Renamed ``tot_press`` parameter to ``total_press``
17531759
17541760
"""
1755-
return mixing_ratio._nounit(saturation_vapor_pressure._nounit(temperature), total_press)
1761+
e_s = saturation_vapor_pressure._nounit(temperature)
1762+
undefined = e_s >= total_press
1763+
if np.any(undefined):
1764+
_warnings.warn('Saturation mixing ratio is undefined for some requested pressure/'
1765+
'temperature combinations. Total pressure must be greater than the '
1766+
'water vapor saturation pressure for liquid water to be in '
1767+
'equilibrium.')
1768+
return np.where(undefined, np.nan, mixing_ratio._nounit(e_s, total_press))
17561769

17571770

17581771
@exporter.export

tests/calc/test_thermo.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ def test_moist_lapse_starting_points(start, direction):
259259
@pytest.mark.filterwarnings('ignore:overflow encountered in exp:RuntimeWarning')
260260
@pytest.mark.filterwarnings(r'ignore:invalid value encountered in \w*divide:RuntimeWarning')
261261
@pytest.mark.filterwarnings(r'ignore:.*Excess accuracy requested.*:UserWarning')
262+
@pytest.mark.filterwarnings(r'ignore:Saturation mixing ratio is undefined.*:UserWarning')
262263
def test_moist_lapse_failure():
263264
"""Test moist_lapse under conditions that cause the ODE solver to fail."""
264265
p = np.logspace(3, -1, 10) * units.hPa
@@ -829,6 +830,14 @@ def test_saturation_mixing_ratio_with_xarray():
829830
xr.testing.assert_identical(result['x'], temperature['x'])
830831

831832

833+
def test_saturation_mixing_ratio_bad_value_handling():
834+
"""Test that saturation mixing ratio issues a warning and returns nan with bad values."""
835+
with pytest.warns(UserWarning, match='undefined'):
836+
e_s = saturation_mixing_ratio(10 * units.hPa, 295 * units.kelvin)
837+
838+
assert np.isnan(e_s)
839+
840+
832841
def test_equivalent_potential_temperature():
833842
"""Test equivalent potential temperature calculation."""
834843
p = 1000 * units.mbar

0 commit comments

Comments
 (0)