1414
1515import scipy .integrate as si
1616import scipy .optimize as so
17+ from scipy .special import lambertw
1718import xarray as xr
1819
1920from .exceptions import InvalidSoundingError
@@ -276,7 +277,9 @@ def water_latent_heat_melting(temperature):
276277
277278@exporter .export
278279@preprocess_and_wrap (wrap_like = 'temperature' , broadcast = ('temperature' , 'dewpoint' ))
279- @check_units ('[temperature]' , '[temperature]' )
280+ @process_units (input_dimensionalities = {'temperature' : '[temperature]' ,
281+ 'dewpoint' : '[temperature]' },
282+ output_dimensionalities = '[dimensionless]' )
280283def relative_humidity_from_dewpoint (temperature , dewpoint , * , phase = 'liquid' ):
281284 r"""Calculate the relative humidity.
282285
@@ -321,8 +324,8 @@ def relative_humidity_from_dewpoint(temperature, dewpoint, *, phase='liquid'):
321324
322325 """
323326 validate_choice ({'liquid' , 'solid' , 'auto' }, phase = phase )
324- e = saturation_vapor_pressure (dewpoint , phase = phase )
325- e_s = saturation_vapor_pressure (temperature , phase = phase )
327+ e = saturation_vapor_pressure . _nounit (dewpoint , phase = phase )
328+ e_s = saturation_vapor_pressure . _nounit (temperature , phase = phase )
326329 return e / e_s
327330
328331
@@ -657,7 +660,7 @@ def dt(p, t):
657660 {'pressure' : '[pressure]' , 'temperature' : '[temperature]' , 'dewpoint' : '[temperature]' },
658661 ('[pressure]' , '[temperature]' )
659662)
660- def lcl (pressure , temperature , dewpoint , max_iters = 50 , eps = 1e-5 ):
663+ def lcl (pressure , temperature , dewpoint , max_iters = None , eps = None ):
661664 r"""Calculate the lifted condensation level (LCL) from the starting point.
662665
663666 The starting state for the parcel is defined by `temperature`, `dewpoint`,
@@ -683,60 +686,57 @@ def lcl(pressure, temperature, dewpoint, max_iters=50, eps=1e-5):
683686 `pint.Quantity`
684687 LCL temperature
685688
686- Other Parameters
687- ----------------
688- max_iters : int, optional
689- The maximum number of iterations to use in calculation, defaults to 50.
690-
691- eps : float, optional
692- The desired relative error in the calculated value, defaults to 1e-5.
693-
694689 Examples
695690 --------
696691 >>> from metpy.calc import lcl
697692 >>> from metpy.units import units
698693 >>> lcl(943 * units.hPa, 33 * units.degC, 28 * units.degC)
699- (<Quantity(877.097306 , 'hectopascal')>, <Quantity(26.7279778 , 'degree_Celsius')>)
694+ (<Quantity(877.033549 , 'hectopascal')>, <Quantity(26.7591908 , 'degree_Celsius')>)
700695
701696 See Also
702697 --------
703698 parcel_profile
704699
705700 Notes
706701 -----
707- This function is implemented using an iterative approach to solve for the
708- LCL. The basic algorithm is:
702+ From [Romps2017]_, this directly solves for the temperature at the LCL, Eq 22a,
703+
704+ .. math:: T_{LCL} = c [W_{-1}(RH_l^{1/a} c \exp{c})]^{-1} T
709705
710- 1. Find the dewpoint from the LCL pressure and starting mixing ratio
711- 2. Find the LCL pressure from the starting temperature and dewpoint
712- 3. Iterate until convergence
706+ and the pressure at the LCL, Eq 22b,
713707
714- The function is guaranteed to finish by virtue of the `max_iters` counter.
708+ .. math:: p_{LCL} = p \left( \frac{T_{LCL}}{T} \right)^{c_{pm} / R_m}
709+
710+ where :math:`a` (Eq 22d), :math:`b` (Eq 22e), and :math:`c` (Eq 22f) are derived constants,
711+ and :math:`W_{-1}` is the :math:`k=-1` branch of the Lambert :math:`W` function.
715712
716713 .. versionchanged:: 1.0
717714 Renamed ``dewpt`` parameter to ``dewpoint``
718715
719716 """
720- def _lcl_iter (p , p0 , w , t ):
721- nonlocal nan_mask
722- td = globals ()['dewpoint' ]._nounit (vapor_pressure ._nounit (p , w ))
723- p_new = (p0 * (td / t ) ** (1. / mpconsts .nounit .kappa ))
724- nan_mask = nan_mask | np .isnan (p_new )
725- return np .where (np .isnan (p_new ), p , p_new )
717+ if max_iters or eps :
718+ _warnings .warn (
719+ 'max_iters, eps arguments unused and will be deprecated in a future version.' ,
720+ PendingDeprecationWarning )
721+
722+ q = specific_humidity_from_dewpoint ._nounit (pressure , dewpoint , phase = 'liquid' )
723+ moist_heat_ratio = (moist_air_specific_heat_pressure ._nounit (q )
724+ / moist_air_gas_constant ._nounit (q ))
725+ spec_heat_diff = mpconsts .nounit .Cp_l - mpconsts .nounit .Cp_v
726+
727+ a = moist_heat_ratio + spec_heat_diff / mpconsts .nounit .Rv
728+ b = (- (mpconsts .nounit .Lv + spec_heat_diff * mpconsts .nounit .T0 )
729+ / (mpconsts .nounit .Rv * temperature ))
730+ c = b / a
726731
727- # Handle nans by creating a mask that gets set by our _lcl_iter function if it
728- # ever encounters a nan, at which point pressure is set to p, stopping iteration.
729- nan_mask = False
730- w = mixing_ratio ._nounit (saturation_vapor_pressure ._nounit (dewpoint ), pressure )
731- lcl_p = so .fixed_point (_lcl_iter , pressure , args = (pressure , w , temperature ),
732- xtol = eps , maxiter = max_iters )
733- lcl_p = np .where (nan_mask , np .nan , lcl_p )
732+ w_minus1 = lambertw (
733+ (relative_humidity_from_dewpoint ._nounit (temperature , dewpoint , phase = 'liquid' )
734+ ** (1 / a ) * c * np .exp (c )), k = - 1 ).real
734735
735- # np.isclose needed if surface is LCL due to precision error with np.log in dewpoint.
736- # Causes issues with parcel_profile_with_lcl if removed. Issue #1187
737- lcl_p = np .where (np .isclose (lcl_p , pressure ), pressure , lcl_p )
736+ t_lcl = c / w_minus1 * temperature
737+ p_lcl = pressure * (t_lcl / temperature ) ** moist_heat_ratio
738738
739- return lcl_p , globals ()[ 'dewpoint' ]. _nounit ( vapor_pressure . _nounit ( lcl_p , w ))
739+ return p_lcl , t_lcl
740740
741741
742742@exporter .export
@@ -912,7 +912,7 @@ def lfc(pressure, temperature, dewpoint, parcel_temperature_profile=None, dewpoi
912912 >>> Td = dewpoint_from_relative_humidity(T, rh)
913913 >>> # calculate LFC
914914 >>> lfc(p, T, Td)
915- (<Quantity(967.153333 , 'hectopascal')>, <Quantity(25.7463935 , 'degree_Celsius')>)
915+ (<Quantity(967.309996 , 'hectopascal')>, <Quantity(25.778387 , 'degree_Celsius')>)
916916
917917 See Also
918918 --------
@@ -1128,7 +1128,7 @@ def el(pressure, temperature, dewpoint, parcel_temperature_profile=None, which='
11281128 >>> prof = parcel_profile(p, T[0], Td[0]).to('degC')
11291129 >>> # calculate EL
11301130 >>> el(p, T, Td, prof)
1131- (<Quantity(112.330791 , 'hectopascal')>, <Quantity(-76.2072049 , 'degree_Celsius')>)
1131+ (<Quantity(112.252054 , 'hectopascal')>, <Quantity(-76.2210312 , 'degree_Celsius')>)
11321132
11331133 See Also
11341134 --------
@@ -1223,12 +1223,12 @@ def parcel_profile(pressure, temperature, dewpoint):
12231223 >>> Td = dewpoint_from_relative_humidity(T, rh)
12241224 >>> # computer parcel temperature
12251225 >>> parcel_profile(p, T[0], Td[0]).to('degC')
1226- <Quantity([ 29.3 28.61221952 25.16541995 23.4015316 21.52129482
1227- 19.50726582 17.33772233 14.98517962 12.41415852 9.57784164
1228- 6.41283006 2.83063069 -1.29662004 -6.16091134 -12.0618903
1229- -19.47976398 -29.16872218 -42.15825511 -50.22699729 -59.51873404
1230- -70.22585491 -82.71535699 -94.46901701 -101.15645164 -108.5667789
1231- -116.92064777 -126.57020709 -138.13646516 -144.98953102 -152.90542218 ], 'degree_Celsius')>
1226+ <Quantity([ 29.3 28.61221952 25.17408111 23.41044641 21.53049669
1227+ 19.51679547 17.34763012 14.99552875 12.4250297 9.58933992
1228+ 6.4250951 2.84385238 -1.28217807 -6.14487817 -12.0437512
1229+ -19.45887455 -29.14459155 -42.13147376 -50.19971377 -59.49169312
1230+ -70.19973455 -82.69068969 -94.44583924 -101.13413664 -108.54542355
1231+ -116.90037584 -126.55118719 -138.11894611 -144.97290122 -152.88981956 ], 'degree_Celsius')>
12321232
12331233 See Also
12341234 --------
@@ -2786,7 +2786,7 @@ def cape_cin(pressure, temperature, dewpoint, parcel_profile, which_lfc='bottom'
27862786 >>> prof = parcel_profile(p, T[0], Td[0]).to('degC')
27872787 >>> # calculate surface based CAPE/CIN
27882788 >>> cape_cin(p, T, Td, prof)
2789- (<Quantity(4818.6911 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
2789+ (<Quantity(4830.74608 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
27902790
27912791 See Also
27922792 --------
@@ -3427,7 +3427,7 @@ def most_unstable_cape_cin(pressure, temperature, dewpoint, **kwargs):
34273427 >>> Td = dewpoint_from_relative_humidity(T, rh)
34283428 >>> # calculate most unstbale CAPE/CIN
34293429 >>> most_unstable_cape_cin(p, T, Td)
3430- (<Quantity(4818.6911 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
3430+ (<Quantity(4830.74608 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
34313431
34323432 See Also
34333433 --------
@@ -3503,7 +3503,7 @@ def mixed_layer_cape_cin(pressure, temperature, dewpoint, **kwargs):
35033503 >>> # calculate dewpoint
35043504 >>> Td = dewpoint_from_relative_humidity(T, rh)
35053505 >>> mixed_layer_cape_cin(p, T, Td, depth=50 * units.hPa)
3506- (<Quantity(691.939088 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
3506+ (<Quantity(678.967843 , 'joule / kilogram')>, <Quantity(0, 'joule / kilogram')>)
35073507
35083508 See Also
35093509 --------
@@ -3586,10 +3586,10 @@ def downdraft_cape(pressure, temperature, dewpoint):
35863586 >>> # calculate dewpoint
35873587 >>> Td = dewpoint_from_relative_humidity(T, rh)
35883588 >>> downdraft_cape(p, T, Td)
3589- (<Quantity(1222.00699 , 'joule / kilogram')>, <Quantity([1008. 1000. 950.
3590- 900. 850. 800. 750. 700. 650. 600.], 'hectopascal')>, <Quantity([17.51354413
3591- 17.21031876 15.24077208 13.12928933 10.85340448 8.38511286 5.6890572 2.71997016
3592- -0.58085433 -4.29043969 ], 'degree_Celsius')>)
3589+ (<Quantity(1220.67097 , 'joule / kilogram')>, <Quantity([1008. 1000. 950.
3590+ 900. 850. 800. 750. 700. 650. 600.], 'hectopascal')>, <Quantity([17.52017969
3591+ 17.21699119 15.24769167 13.13648889 10.86092343 8.39299889 5.69736825 2.72877681
3592+ -0.57146657 -4.28036902 ], 'degree_Celsius')>)
35933593
35943594 See Also
35953595 --------
@@ -4387,7 +4387,7 @@ def wet_bulb_temperature(pressure, temperature, dewpoint):
43874387 >>> from metpy.calc import wet_bulb_temperature
43884388 >>> from metpy.units import units
43894389 >>> wet_bulb_temperature(993 * units.hPa, 32 * units.degC, 15 * units.degC)
4390- <Quantity(20.3746466 , 'degree_Celsius')>
4390+ <Quantity(20.3937601 , 'degree_Celsius')>
43914391
43924392 See Also
43934393 --------
@@ -4824,7 +4824,7 @@ def lifted_index(pressure, temperature, parcel_profile, vertical_dim=0):
48244824 >>>
48254825 >>> # Calculate lifted index using our mixed profile
48264826 >>> lifted_index(press, temp, mixed_prof)
4827- <Quantity([2.51432431 ], 'delta_degree_Celsius')>
4827+ <Quantity([2.54198585 ], 'delta_degree_Celsius')>
48284828
48294829 See Also
48304830 --------
@@ -5227,7 +5227,7 @@ def showalter_index(pressure, temperature, dewpoint):
52275227 >>> Td = dewpoint_from_relative_humidity(T, rh)
52285228 >>> # compute the showalter index
52295229 >>> showalter_index(p, T, Td)
5230- <Quantity([0.5107429 ], 'delta_degree_Celsius')>
5230+ <Quantity([0.51436699 ], 'delta_degree_Celsius')>
52315231
52325232 """
52335233 # find the measured temperature and dew point temperature at 850 hPa.
0 commit comments