diff --git a/docs/release_notes/index.rst b/docs/release_notes/index.rst index 3e54796..8b1337b 100644 --- a/docs/release_notes/index.rst +++ b/docs/release_notes/index.rst @@ -4,5 +4,6 @@ Release notes .. toctree:: :maxdepth: 1 + version_0.3_updates version_0.2_updates version_0.1_updates diff --git a/docs/release_notes/version_0.3_updates.rst b/docs/release_notes/version_0.3_updates.rst new file mode 100644 index 0000000..cc22913 --- /dev/null +++ b/docs/release_notes/version_0.3_updates.rst @@ -0,0 +1,14 @@ +Version 0.3 Updates +///////////////////////// + + +Version 0.3.0 +=============== + +- Removed the ``eps`` and ``out`` keywords arguments to control zero humidity in + + - :py:meth:`dewpoint_from_specific_humidity ` + - :py:meth:`dewpoint_from_relative_humidity ` + - :py:meth:`temperature_from_saturation_vapour_pressure ` + + This was done to revert the changes added in version 0.2.0. diff --git a/src/earthkit/meteo/thermo/array/thermo.py b/src/earthkit/meteo/thermo/array/thermo.py index 4155d1a..8eaa0de 100644 --- a/src/earthkit/meteo/thermo/array/thermo.py +++ b/src/earthkit/meteo/thermo/array/thermo.py @@ -263,21 +263,9 @@ def compute_slope(self, t, phase): elif phase == "ice": return self._es_ice_slope(t) - def t_from_es(self, es, eps=1e-8, out=None): - def _comp(x): - x = x / self.c1 - x = np.log(x) - return (x * self.c4w - self.c3w * self.t0) / (x - self.c3w) - - if out is not None: - v = np.asarray(es) - z_mask = v < eps - v_mask = ~z_mask - v[v_mask] = _comp(v[v_mask]) - v[z_mask] = out - return v - else: - return _comp(es) + def t_from_es(self, es): + v = np.log(es / self.c1) + return (v * self.c4w - self.c3w * self.t0) / (v - self.c3w) def _es_water(self, t): return self.c1 * np.exp(self.c3w * (t - self.t0) / (t - self.c4w)) @@ -572,27 +560,18 @@ def saturation_specific_humidity_slope(t, p, es=None, es_slope=None, phase="mixe return constants.epsilon * es_slope * p / v -def temperature_from_saturation_vapour_pressure(es, eps=1e-8, out=None): +def temperature_from_saturation_vapour_pressure(es): r"""Compute the temperature from saturation vapour pressure. Parameters ---------- es: ndarray :func:`saturation_vapour_pressure` (Pa) - eps: number - If ``out`` is not None, return ``out`` when ``es`` < ``eps``. If out - is None, ``eps`` is ignored and return np.nan for ``es`` values very - close to zero. *New in version 0.2.0* - out: number or None - If not None, return ``out`` when ``es`` < ``eps``. If None, ``eps`` is - ignored and return np.nan for ``es`` values very close to zero. - *New in version 0.2.0* - Returns ------- ndarray - Temperature (K) + Temperature (K). For zero ``es`` values returns np.nan. The computation is always based on the "water" phase of @@ -600,7 +579,7 @@ def temperature_from_saturation_vapour_pressure(es, eps=1e-8, out=None): phase ``es`` was computed to. """ - return _EsComp().t_from_es(es, eps=eps, out=out) + return _EsComp().t_from_es(es) def relative_humidity_from_dewpoint(t, td): @@ -775,7 +754,7 @@ def specific_humidity_from_relative_humidity(t, r, p): return specific_humidity_from_vapour_pressure(e, p) -def dewpoint_from_relative_humidity(t, r, eps=1e-8, out=None): +def dewpoint_from_relative_humidity(t, r): r"""Compute the dewpoint temperature from relative humidity. Parameters @@ -784,19 +763,11 @@ def dewpoint_from_relative_humidity(t, r, eps=1e-8, out=None): Temperature (K) r: ndarray Relative humidity (%) - eps: number - If ``out`` is not None, return ``out`` when ``r`` < ``eps``. - If out is None, ``eps`` is ignored and return np.nan for ``r`` - values very close to zero. *New in version 0.2.0* - out: number or None - If not None, return ``out`` when ``r`` < ``eps``. If None, ``eps`` is - ignored and return np.nan for ``r`` values very close to zero. - *New in version 0.2.0* Returns ------- ndarray - Dewpoint temperature (K) + Dewpoint temperature (K). For zero ``r`` values returns np.nan. The computation starts with determining the the saturation vapour pressure over @@ -815,24 +786,11 @@ def dewpoint_from_relative_humidity(t, r, eps=1e-8, out=None): equations used in :func:`saturation_vapour_pressure`. """ - # by masking upfront we avoid RuntimeWarning when calling log() in - # the computation of td when r is very small - if out is not None: - r = np.asarray(r) - mask = r < eps - r[mask] = np.nan - es = saturation_vapour_pressure(t, phase="water") * r / 100.0 - td = temperature_from_saturation_vapour_pressure(es) + return temperature_from_saturation_vapour_pressure(es) - if out is not None and not np.isnan(out): - td = np.asarray(td) - td[mask] = out - return td - - -def dewpoint_from_specific_humidity(q, p, eps=1e-8, out=None): +def dewpoint_from_specific_humidity(q, p): r"""Compute the dewpoint temperature from specific humidity. Parameters @@ -841,19 +799,11 @@ def dewpoint_from_specific_humidity(q, p, eps=1e-8, out=None): Specific humidity (kg/kg) p: ndarray Pressure (Pa) - eps: number - If ``out`` is not None, return ``out`` when ``q`` < ``eps``. - If out is None, ``eps`` is ignored and return np.nan for ``q`` - values very close to zero. *New in version 0.2.0* - out: number or None - If not None, return ``out`` when ``q`` < ``eps``. If None, ``eps`` is - ignored and return np.nan for ``q`` values very close to zero. - *New in version 0.2.0* Returns ------- ndarray - Dewpoint temperature (K) + Dewpoint temperature (K). For zero ``q`` values returns np.nan. The computation starts with determining the the saturation vapour pressure over @@ -873,21 +823,7 @@ def dewpoint_from_specific_humidity(q, p, eps=1e-8, out=None): used in :func:`saturation_vapour_pressure`. """ - # by masking upfront we avoid RuntimeWarning when calling log() in - # the computation of td when q is very small - if out is not None: - q = np.asarray(q) - mask = q < eps - q[mask] = np.nan - - es = vapour_pressure_from_specific_humidity(q, p) - td = temperature_from_saturation_vapour_pressure(es) - - if out is not None and not np.isnan(out): - td = np.asarray(td) - td[mask] = out - - return td + return temperature_from_saturation_vapour_pressure(vapour_pressure_from_specific_humidity(q, p)) def virtual_temperature(t, q): diff --git a/tests/thermo/test_thermo_array.py b/tests/thermo/test_thermo_array.py index a87fcf8..073f8c5 100644 --- a/tests/thermo/test_thermo_array.py +++ b/tests/thermo/test_thermo_array.py @@ -334,24 +334,20 @@ def test_temperature_from_saturation_vapour_pressure_1(): @pytest.mark.parametrize( - "es,kwargs,expected_values", + "es,expected_values", [ - (4.2, {}, 219.7796336743947), - ([4.2, 0, 0.001, np.nan], {"eps": 1e-2, "out": 100}, [219.7796336743947, 100, 100, np.nan]), - ([4.2, 0, 0.001, np.nan], {"eps": 1e-2, "out": np.nan}, [219.7796336743947, np.nan, np.nan, np.nan]), - (0, {}, np.nan), - (0.001, {"eps": 1e-2, "out": 100}, 100.0), - (0.001, {"eps": 1e-2, "out": np.nan}, np.nan), + (4.2, 219.7796336743947), + (0, np.nan), ], ) -def test_temperature_from_saturation_vapour_pressure_2(es, kwargs, expected_values): +def test_temperature_from_saturation_vapour_pressure_2(es, expected_values): multi = isinstance(es, list) if multi: es = np.array(es) expected_values = np.array(expected_values) - t = thermo.array.temperature_from_saturation_vapour_pressure(es, **kwargs) + t = thermo.array.temperature_from_saturation_vapour_pressure(es) if multi: np.testing.assert_allclose(t, expected_values, equal_nan=True) else: @@ -448,7 +444,7 @@ def test_specific_humidity_from_relative_humidity(): @pytest.mark.parametrize( - "t,r,kwargs,expected_values", + "t,r,expected_values", [ ( [20.0, 20, 0, 35, 5, -15, 25, 25], @@ -462,37 +458,12 @@ def test_specific_humidity_from_relative_humidity(): 15.4779832381, 0, ], - {}, [20.0, 10, -10, 32, -15, -24, -3, np.nan], ), - ( - [20.0, 20.0, 20.0], - [ - 52.5224541378, - 0.0, - 0.000001, - ], - {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, - [10, 100, 100], - ), - ( - [20.0, 20.0, 20.0], - [ - 52.5224541378, - 0.0, - 0.000001, - ], - {"eps": 1e-3, "out": np.nan}, - [10, np.nan, np.nan], - ), - (20.0, 52.5224541378, {}, 10.0), - (20.0, 0.0, {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, 100), - (20.0, 0.000001, {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, 100), - (20.0, 0.0, {"eps": 1e-3, "out": np.nan}, np.nan), - (20, 0.000001, {"eps": 1e-3, "out": np.nan}, np.nan), + (20.0, 52.5224541378, 10.0), ], ) -def test_dewpoint_from_relative_humidity(t, r, kwargs, expected_values): +def test_dewpoint_from_relative_humidity(t, r, expected_values): # reference was tested with an online relhum calculator at: # https://bmcnoldy.rsmas.miami.edu/Humidity.html @@ -505,7 +476,7 @@ def test_dewpoint_from_relative_humidity(t, r, kwargs, expected_values): t = thermo.array.celsius_to_kelvin(t) v_ref = thermo.array.celsius_to_kelvin(expected_values) - td = thermo.array.dewpoint_from_relative_humidity(t, r, **kwargs) + td = thermo.array.dewpoint_from_relative_humidity(t, r) if multi: assert np.allclose(td, v_ref, equal_nan=True) else: @@ -513,42 +484,17 @@ def test_dewpoint_from_relative_humidity(t, r, kwargs, expected_values): @pytest.mark.parametrize( - "q,p,kwargs,expected_values", + "q,p,expected_values", [ ( [0.0169461501, 0.0155840075, 0.0134912382, 0.0083409720, 0.0057268584, 0.0025150791, 0], [967.5085, 936.3775, 872.248, 756.1647, 649.157, 422.4207, 422.4207], - {}, [21.78907, 19.90885, 16.50236, 7.104064, -0.3548709, -16.37916, np.nan], ), - ( - [ - 0.0169461501, - 0.0, - 0.000001, - ], - [967.5085, 967.5085, 967.5085], - {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, - [21.78907, 100, 100], - ), - ( - [ - 0.0169461501, - 0.0, - 0.000001, - ], - [967.5085, 967.5085, 967.5085], - {"eps": 1e-3, "out": np.nan}, - [21.78907, np.nan, np.nan], - ), - (0.0169461501, 967.508, {}, 21.78907), - (0.0, 967.5085, {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, 100), - (0.000001, 967.5085, {"eps": 1e-3, "out": thermo.array.celsius_to_kelvin(100)}, 100), - (0.0, 967.5085, {"eps": 1e-3, "out": np.nan}, np.nan), - (0.000001, 967.5085, {"eps": 1e-3, "out": np.nan}, np.nan), + (0.0169461501, 967.508, 21.78907), ], ) -def test_dewpoint_from_specific_humidity(q, p, kwargs, expected_values): +def test_dewpoint_from_specific_humidity(q, p, expected_values): multi = isinstance(q, list) if multi: q = np.array(q) @@ -558,7 +504,7 @@ def test_dewpoint_from_specific_humidity(q, p, kwargs, expected_values): p = p * 100.0 v_ref = thermo.array.celsius_to_kelvin(expected_values) - td = thermo.array.dewpoint_from_specific_humidity(q, p, **kwargs) + td = thermo.array.dewpoint_from_specific_humidity(q, p) if multi: assert np.allclose(td, v_ref, equal_nan=True) else: