@@ -1711,17 +1711,51 @@ def dewpoint_from_relative_humidity(temperature, relative_humidity):
17111711 return dewpoint (relative_humidity * saturation_vapor_pressure (temperature ))
17121712
17131713
1714+ def _dewpoint_ambaum (vapor_pressure ):
1715+ """Calculate dewpoint by numerically inverting Ambaum (2020) formula."""
1716+ def objective (temp_kelvin , target_pressure ):
1717+ """Calculate difference between saturation pressure at temp and target."""
1718+ latent_heat = (mpconsts .nounit .Lv
1719+ - (mpconsts .nounit .Cp_l - mpconsts .nounit .Cp_v )
1720+ * (temp_kelvin - mpconsts .nounit .T0 ))
1721+ heat_power = (mpconsts .nounit .Cp_l - mpconsts .nounit .Cp_v ) / mpconsts .nounit .Rv
1722+ exp_term = ((mpconsts .nounit .Lv / mpconsts .nounit .T0 - latent_heat / temp_kelvin )
1723+ / mpconsts .nounit .Rv )
1724+ e_sat = (mpconsts .nounit .sat_pressure_0c
1725+ * (mpconsts .nounit .T0 / temp_kelvin ) ** heat_power
1726+ * np .exp (exp_term ))
1727+ return e_sat - target_pressure
1728+
1729+ t_min = 182.65 # -90.5°C
1730+ t_max = 373.15 # 100°C
1731+
1732+ if np .isscalar (vapor_pressure ) or vapor_pressure .size == 1 :
1733+ return so .brentq (objective , t_min , t_max , args = (vapor_pressure ,))
1734+ else :
1735+ vapor_pressure_flat = vapor_pressure .flatten ()
1736+ result_flat = np .empty_like (vapor_pressure_flat )
1737+ for i , vp in enumerate (vapor_pressure_flat ):
1738+ result_flat [i ] = so .brentq (objective , t_min , t_max , args = (vp ,))
1739+ return result_flat .reshape (vapor_pressure .shape )
1740+
1741+
17141742@exporter .export
17151743@preprocess_and_wrap (wrap_like = 'vapor_pressure' )
17161744@process_units ({'vapor_pressure' : '[pressure]' }, '[temperature]' , output_to = units .degC )
1717- def dewpoint (vapor_pressure ):
1745+ def dewpoint (vapor_pressure , method = 'bolton' ):
17181746 r"""Calculate the ambient dewpoint given the vapor pressure.
17191747
17201748 Parameters
17211749 ----------
17221750 vapor_pressure : `pint.Quantity`
17231751 Water vapor partial pressure
17241752
1753+ method : str, optional
1754+ Formula to use for calculation. Options are:
1755+ - 'bolton' (default): Analytical inversion of Bolton (1980) formula
1756+ - 'ambaum': Numerical inversion of Ambaum (2020) formula, consistent
1757+ with :func:`saturation_vapor_pressure`
1758+
17251759 Returns
17261760 -------
17271761 `pint.Quantity`
@@ -1734,24 +1768,41 @@ def dewpoint(vapor_pressure):
17341768 >>> dewpoint(22 * units.hPa)
17351769 <Quantity(19.0291018, 'degree_Celsius')>
17361770
1771+ Using the Ambaum method for consistency with saturation_vapor_pressure:
1772+
1773+ >>> dewpoint(22 * units.hPa, method='ambaum')
1774+ <Quantity(19.0426679, 'degree_Celsius')>
1775+
17371776 See Also
17381777 --------
17391778 dewpoint_from_relative_humidity, saturation_vapor_pressure, vapor_pressure
17401779
17411780 Notes
17421781 -----
1743- This function inverts the [Bolton1980]_ formula for saturation vapor
1744- pressure to instead calculate the temperature. This yields the following formula for
1745- dewpoint in degrees Celsius, where :math:`e` is the ambient vapor pressure in millibars:
1782+ The default 'bolton' method inverts the [Bolton1980]_ formula:
17461783
17471784 .. math:: T = \frac{243.5 \log(e / 6.112)}{17.67 - \log(e / 6.112)}
17481785
1786+ The 'ambaum' method numerically inverts the [Ambaum2020]_ formula used by
1787+ :func:`saturation_vapor_pressure`, ensuring thermodynamic consistency
1788+ where ``dewpoint(saturation_vapor_pressure(T), method='ambaum') == T``.
1789+
17491790 .. versionchanged:: 1.0
17501791 Renamed ``e`` parameter to ``vapor_pressure``
17511792
1793+ .. versionchanged:: 1.8
1794+ Added ``method`` parameter with 'ambaum' option for consistency with
1795+ :func:`saturation_vapor_pressure`.
1796+
17521797 """
1753- val = np .log (vapor_pressure / mpconsts .nounit .sat_pressure_0c )
1754- return mpconsts .nounit .zero_degc + 243.5 * val / (17.67 - val )
1798+ if method == 'ambaum' :
1799+ return _dewpoint_ambaum (vapor_pressure )
1800+ elif method == 'bolton' :
1801+ # Original Bolton (1980) analytical formula
1802+ val = np .log (vapor_pressure / mpconsts .nounit .sat_pressure_0c )
1803+ return mpconsts .nounit .zero_degc + 243.5 * val / (17.67 - val )
1804+ else :
1805+ raise ValueError (f"Unknown method '{ method } '. Must be 'bolton' or 'ambaum'." )
17551806
17561807
17571808@exporter .export
0 commit comments