diff --git a/src/metpy/calc/thermo.py b/src/metpy/calc/thermo.py index 48f18d4a6fa..eb5958978bc 100644 --- a/src/metpy/calc/thermo.py +++ b/src/metpy/calc/thermo.py @@ -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 diff --git a/tests/calc/test_thermo.py b/tests/calc/test_thermo.py index 2fa8575fc9f..a6ef1bfbff3 100644 --- a/tests/calc/test_thermo.py +++ b/tests/calc/test_thermo.py @@ -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 @@ -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