Skip to content

Commit

Permalink
ENH: Avoid returning negative saturation mixing ratios
Browse files Browse the repository at this point in the history
This corresponds to being outside the region of liquid phase water
equilibrium, where e_s >= p. Detect this case, warn, and instead return
NaN.
  • Loading branch information
dopplershift committed Feb 14, 2025
1 parent ca236e5 commit 8b7dc19
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/metpy/calc/thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1748,11 +1748,24 @@ def saturation_mixing_ratio(total_press, temperature):
.. math:: r_s = \epsilon \frac{e_s}{p - e_s}
By definition, this value is only defined for conditions where the saturation vapor
pressure (:math:`e_s`) for the given temperature is less than the given total pressure
(:math:`p`). Otherwise, liquid phase water cannot exist in equilibrium and there is only
water vapor present. For any value pairs that fall under this condition, the function will
warn and return NaN.
.. versionchanged:: 1.0
Renamed ``tot_press`` parameter to ``total_press``
"""
return mixing_ratio._nounit(saturation_vapor_pressure._nounit(temperature), total_press)
e_s = saturation_vapor_pressure._nounit(temperature)
undefined = e_s >= total_press
if np.any(undefined):
_warnings.warn('Saturation mixing ratio is undefined for some requested pressure/'
'temperature combinations. Total pressure must be greater than the '
'water vapor saturation pressure for liquid water to be in '
'equilibrium.')
return np.where(undefined, np.nan, mixing_ratio._nounit(e_s, total_press))


@exporter.export
Expand Down
9 changes: 9 additions & 0 deletions tests/calc/test_thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ def test_moist_lapse_starting_points(start, direction):
@pytest.mark.filterwarnings('ignore:overflow encountered in exp:RuntimeWarning')
@pytest.mark.filterwarnings(r'ignore:invalid value encountered in \w*divide:RuntimeWarning')
@pytest.mark.filterwarnings(r'ignore:.*Excess accuracy requested.*:UserWarning')
@pytest.mark.filterwarnings(r'ignore:Saturation mixing ratio is undefined.*:UserWarning')
def test_moist_lapse_failure():
"""Test moist_lapse under conditions that cause the ODE solver to fail."""
p = np.logspace(3, -1, 10) * units.hPa
Expand Down Expand Up @@ -829,6 +830,14 @@ def test_saturation_mixing_ratio_with_xarray():
xr.testing.assert_identical(result['x'], temperature['x'])


def test_saturation_mixing_ratio_bad_value_handling():
"""Test that saturation mixing ratio issues a warning and returns nan with bad values."""
with pytest.warns(UserWarning, match='undefined'):
e_s = saturation_mixing_ratio(10 * units.hPa, 295 * units.kelvin)

assert np.isnan(e_s)


def test_equivalent_potential_temperature():
"""Test equivalent potential temperature calculation."""
p = 1000 * units.mbar
Expand Down

0 comments on commit 8b7dc19

Please sign in to comment.