From 988b52164b48fc89aa043adb7af822dae4f0c4f4 Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Mon, 23 Dec 2024 11:11:19 -0700 Subject: [PATCH 01/10] Add triple-point and L_s Add necessary constants for new calculations. Fix weird malformed table errors with units column spaces. --- src/metpy/constants/__init__.py | 30 ++++++++++++++++-------------- src/metpy/constants/default.py | 2 ++ 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/metpy/constants/__init__.py b/src/metpy/constants/__init__.py index 9c2aab04b8a..ee8228be7ee 100644 --- a/src/metpy/constants/__init__.py +++ b/src/metpy/constants/__init__.py @@ -22,20 +22,22 @@ Water ----- -======================= ================ ========== ============================ ==================================================== -Name Symbol Short Name Units Description ------------------------ ---------------- ---------- ---------------------------- ---------------------------------------------------- -water_molecular_weight :math:`M_w` Mw :math:`\text{g mol}^{-1}` Molecular weight of water [5]_ -water_gas_constant :math:`R_v` Rv :math:`\text{J (K kg)}^{-1}` Gas constant for water vapor [2]_ [5]_ -density_water :math:`\rho_l` rho_l :math:`\text{kg m}^{-3}` Maximum recommended density of liquid water, 0-40C [5]_ -wv_specific_heat_press :math:`C_{pv}` Cp_v :math:`\text{J (K kg)}^{-1}` Specific heat at constant pressure for water vapor -wv_specific_heat_vol :math:`C_{vv}` Cv_v :math:`\text{J (K kg)}^{-1}` Specific heat at constant volume for water vapor -water_specific_heat :math:`Cp_l` Cp_l :math:`\text{J (K kg)}^{-1}` Specific heat of liquid water at 0C [6]_ -water_heat_vaporization :math:`L_v` Lv :math:`\text{J kg}^{-1}` Latent heat of vaporization for liquid water at 0C [7]_ -water_heat_fusion :math:`L_f` Lf :math:`\text{J kg}^{-1}` Latent heat of fusion for liquid water at 0C [7]_ -ice_specific_heat :math:`C_{pi}` Cp_i :math:`\text{J (K kg)}^{-1}` Specific heat of ice at 0C [7]_ -density_ice :math:`\rho_i` rho_i :math:`\text{kg m}^{-3}` Density of ice at 0C -======================= ================ ========== ============================ ==================================================== +============================== ================ ========== ============================== ========================================================== +Name Symbol Short Name Units Description +------------------------------ ---------------- ---------- ------------------------------ ---------------------------------------------------------- +water_molecular_weight :math:`M_w` Mw :math:`\text{g mol}^{-1}` Molecular weight of water [5]_ +water_gas_constant :math:`R_v` Rv :math:`\text{J (K kg)}^{-1}` Gas constant for water vapor [2]_ [5]_ +density_water :math:`\rho_l` rho_l :math:`\text{kg m}^{-3}` Maximum recommended density of liquid water, 0-40C [5]_ +wv_specific_heat_press :math:`C_{pv}` Cp_v :math:`\text{J (K kg)}^{-1}` Specific heat at constant pressure for water vapor +wv_specific_heat_vol :math:`C_{vv}` Cv_v :math:`\text{J (K kg)}^{-1}` Specific heat at constant volume for water vapor +water_specific_heat :math:`C_{pl}` Cp_l :math:`\text{J (K kg)}^{-1}` Specific heat of liquid water at 0C [6]_ +water_heat_vaporization :math:`L_v` Lv :math:`\text{J kg}^{-1}` Latent heat of vaporization for liquid water at 0C [7]_ +water_heat_fusion :math:`L_f` Lf :math:`\text{J kg}^{-1}` Latent heat of fusion for liquid water at 0C [7]_ +water_heat_sublimation :math:`L_s` Ls :math:`\text{J kg}^{-1}` Latent heat of sublimation for water, Lv + Lf +ice_specific_heat :math:`C_{pi}` Cp_i :math:`\text{J (K kg)}^{-1}` Specific heat of ice at 0C [7]_ +density_ice :math:`\rho_i` rho_i :math:`\text{kg m}^{-3}` Density of ice at 0C +water_triple_point_temperature :math:`T_0` T0 :math:`\text{K}` Triple-point temperature of water [2]_ +============================== ================ ========== ============================== ========================================================== Dry Air ------- diff --git a/src/metpy/constants/default.py b/src/metpy/constants/default.py index 28c9558b01f..c3067d85aac 100644 --- a/src/metpy/constants/default.py +++ b/src/metpy/constants/default.py @@ -37,9 +37,11 @@ Cp_l = water_specific_heat = units.Quantity(4.2194, 'kJ / kg / K').to('J / kg / K') Lv = water_heat_vaporization = units.Quantity(2.50084e6, 'J / kg') Lf = water_heat_fusion = units.Quantity(3.337e5, 'J / kg') + Ls = water_heat_sublimation = Lv + Lf Cp_i = ice_specific_heat = units.Quantity(2090, 'J / kg / K') rho_i = density_ice = units.Quantity(917, 'kg / m^3') sat_pressure_0c = units.Quantity(6.112, 'millibar') + T0 = water_triple_point_temperature = units.Quantity(273.16, 'K') # Dry air Md = dry_air_molecular_weight = units.Quantity(28.96546e-3, 'kg / mol') From e5601b776f92f2d89f916fc213bce5370e4f4dca Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Mon, 23 Dec 2024 11:37:13 -0700 Subject: [PATCH 02/10] Add new refs for equations --- docs/api/references.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api/references.rst b/docs/api/references.rst index dacd4189546..87af83af82f 100644 --- a/docs/api/references.rst +++ b/docs/api/references.rst @@ -2,6 +2,10 @@ References ========== +.. [Ambaum2020] Ambaum MHP, 2020: Accurate, simple equation for saturated vapour pressure over water and ice. + *QJR Meteorol Soc.*; **146**: 4252–4258, + doi: `10.1002/qj.3899 `_. + .. [Anderson2013] Anderson, G. B., M. L. Bell, and R. D. Peng, 2013: Methods to Calculate the Heat Index as an Exposure Metric in Environmental Health Research. *Environmental Health Perspectives*, **121**, 1111-1119, @@ -176,6 +180,10 @@ References .. [Rochette2006] Rochette, Scott M., and Patrick S. Market. "A primer on the ageostrophic wind." Natl. Weather Dig. 30 (2006): 17-28. +.. [Romps2017] Romps, D. M., 2017: Exact Expression for the Lifting Condensation Level. + *J. Atmos. Sci.*, **74**, 3891–3900, + doi: `10.1175/JAS-D-17-0102.1. `_. + .. [Rothfusz1990] Rothfusz, L.P.: *The Heat Index "Equation"*. Fort Worth, TX: Scientific Services Division, NWS Southern Region Headquarters, 1990. `SR90-23 <../_static/rothfusz-1990-heat-index-equation.pdf>`_, 2 pp. From c1005a81f631836cc1cbe7962117ce7cdabc091d Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Mon, 23 Dec 2024 11:39:36 -0700 Subject: [PATCH 03/10] Add non-constant quantities Calculate gas constant and specific heat capacity (and thus kappa) of moist air. Add temperature-dependent latent heat quantity calculations. --- src/metpy/calc/thermo.py | 232 ++++++++++++++++++++++++++++++++++++++ tests/calc/test_thermo.py | 65 ++++++++++- 2 files changed, 291 insertions(+), 6 deletions(-) diff --git a/src/metpy/calc/thermo.py b/src/metpy/calc/thermo.py index 983e1315474..917f0f25518 100644 --- a/src/metpy/calc/thermo.py +++ b/src/metpy/calc/thermo.py @@ -29,6 +29,238 @@ exporter = Exporter(globals()) +@exporter.export +@preprocess_and_wrap(wrap_like='specific_humidity') +@check_units('[dimensionless]') +def moist_air_gas_constant(specific_humidity): + r"""Calculate R_m, the specific gas constant for a parcel of moist air. + + Parameters + ---------- + specific_humidity : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Specific gas constant + + Examples + -------- + >>> from metpy.calc import moist_air_gas_constant + >>> from metpy.units import units + >>> moist_air_gas_constant(11 * units('g/kg')) + + + See Also + -------- + moist_air_specific_heat_pressure, moist_air_poisson_exponent + + Notes + ----- + .. math:: R_m = (1 - q_v) R_a + q_v R_v + + Eq 16, [Romps2017]_ using MetPy-defined constants in place of cited values. + + """ + return ((1 - specific_humidity) * mpconsts.dry_air_gas_constant + + specific_humidity * mpconsts.water_gas_constant) + + +@exporter.export +@preprocess_and_wrap(wrap_like='specific_humidity') +@check_units('[dimensionless]') +def moist_air_specific_heat_pressure(specific_humidity): + r"""Calculate C_pm, the specific heat at constant pressure for a moist air parcel. + + Parameters + ---------- + specific_humidity : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Specific heat capacity of air at constant pressure + + Examples + -------- + >>> from metpy.calc import moist_air_specific_heat_pressure + >>> from metpy.units import units + >>> moist_air_specific_heat_pressure(11 * units('g/kg')) + + + See Also + -------- + moist_air_gas_constant, moist_air_poisson_exponent + + Notes + ----- + .. math:: c_{pm} = (1 - q_v) c_{pa} + q_v c_{pv} + + Eq 17, [Romps2017]_ using MetPy-defined constants in place of cited values. + + """ + return ((1 - specific_humidity) * mpconsts.dry_air_spec_heat_press + + specific_humidity * mpconsts.wv_specific_heat_press) + + +@exporter.export +@preprocess_and_wrap(wrap_like='specific_humidity') +@process_units( + input_dimensionalities={'specific_humidity': 'dimensionless'}, + output_dimensionalities='[dimensionless]' +) +def moist_air_poisson_exponent(specific_humidity): + r"""Calculate kappa_m, the Poisson exponent for a moist air parcel. + + Parameters + ---------- + specific_humidity : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Poisson exponent of moist air parcel + + Examples + -------- + >>> from metpy.calc import moist_air_poisson_exponent + >>> from metpy.units import units + >>> moist_air_poisson_exponent(11 * units('g/kg')) + + + See Also + -------- + moist_air_gas_constant, moist_air_specific_heat_pressure + + """ + return (moist_air_gas_constant._nounit(specific_humidity) + / moist_air_specific_heat_pressure._nounit(specific_humidity)) + + +@exporter.export +@preprocess_and_wrap(wrap_like='temperature') +@check_units('[temperature]') +def water_latent_heat_vaporization(temperature): + r"""Calculate the latent heat of vaporization for water. + + Accounts for variations in latent heat across valid temperature range. + + Parameters + ---------- + temperature : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Latent heat of vaporization + + Examples + -------- + >>> from metpy.calc import water_latent_heat_vaporization + >>> from metpy.units import units + >>> water_latent_heat_vaporization(20 * units.degC) + + + See Also + -------- + water_latent_heat_sublimation, water_latent_heat_melting + + Notes + ----- + Assumption of constant :math:`C_{pv}` limits validity to :math:`0` -- :math:`100^{\circ} C` + range. + + .. math:: L = L_0 - (c_{pl} - c_{pv}) (T - T_0) + + Eq 15, [Ambaum2020]_, using MetPy-defined constants in place of cited values. + + """ + return (mpconsts.water_heat_vaporization + - (mpconsts.water_specific_heat - mpconsts.wv_specific_heat_press) + * (temperature - mpconsts.water_triple_point_temperature)) + + +@exporter.export +@preprocess_and_wrap(wrap_like='temperature') +@check_units('[temperature]') +def water_latent_heat_sublimation(temperature): + r"""Calculate the latent heat of sublimation for water. + + Accounts for variations in latent heat across valid temperature range. + + Parameters + ---------- + temperature : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Latent heat of vaporization + + Examples + -------- + >>> from metpy.calc import water_latent_heat_sublimation + >>> from metpy.units import units + >>> water_latent_heat_sublimation(-15 * units.degC) + + + See Also + -------- + water_latent_heat_vaporization, water_latent_heat_melting + + Notes + ----- + .. math:: L_s = L_{s0} - (c_{pl} - c_{pv}) (T - T_0) + + Eq 18, [Ambaum2020]_, using MetPy-defined constants in place of cited values. + + """ + return (mpconsts.water_heat_sublimation + - (mpconsts.ice_specific_heat - mpconsts.wv_specific_heat_press) + * (temperature - mpconsts.water_triple_point_temperature)) + + +@exporter.export +@preprocess_and_wrap(wrap_like='temperature') +@check_units('[temperature]') +def water_latent_heat_melting(temperature): + r"""Calculate the latent heat of melting for water. + + Accounts for variations in latent heat across valid temperature range. + + Parameters + ---------- + temperature : `pint.Quantity` + + Returns + ------- + `pint.Quantity` + Latent heat of vaporization + + Examples + -------- + >>> from metpy.calc import water_latent_heat_melting + >>> from metpy.units import units + >>> water_latent_heat_melting(-15 * units.degC) + + + See Also + -------- + water_latent_heat_vaporization, water_latent_heat_sublimation + + Notes + ----- + .. math:: L_m = L_{m0} + (c_{pl} - c_{pi}) (T - T_0) + + Body text below Eq 20, [Ambaum2020]_, derived from Eq 15, Eq 18. + Uses MetPy-defined constants in place of cited values. + + """ + return (mpconsts.water_heat_fusion + - (mpconsts.water_specific_heat - mpconsts.ice_specific_heat) + * (temperature - mpconsts.water_triple_point_temperature)) + + @exporter.export @preprocess_and_wrap(wrap_like='temperature', broadcast=('temperature', 'dewpoint')) @check_units('[temperature]', '[temperature]') diff --git a/tests/calc/test_thermo.py b/tests/calc/test_thermo.py index 05add1f17a2..81d96174821 100644 --- a/tests/calc/test_thermo.py +++ b/tests/calc/test_thermo.py @@ -20,11 +20,13 @@ isentropic_interpolation, isentropic_interpolation_as_dataset, k_index, lcl, lfc, lifted_index, mixed_layer, mixed_layer_cape_cin, mixed_parcel, mixing_ratio, mixing_ratio_from_relative_humidity, - mixing_ratio_from_specific_humidity, moist_lapse, moist_static_energy, - most_unstable_cape_cin, most_unstable_parcel, parcel_profile, - parcel_profile_with_lcl, parcel_profile_with_lcl_as_dataset, - potential_temperature, psychrometric_vapor_pressure_wet, - relative_humidity_from_dewpoint, relative_humidity_from_mixing_ratio, + mixing_ratio_from_specific_humidity, moist_air_gas_constant, + moist_air_poisson_exponent, moist_air_specific_heat_pressure, + moist_lapse, moist_static_energy, most_unstable_cape_cin, + most_unstable_parcel, parcel_profile, parcel_profile_with_lcl, + parcel_profile_with_lcl_as_dataset, potential_temperature, + psychrometric_vapor_pressure_wet, relative_humidity_from_dewpoint, + relative_humidity_from_mixing_ratio, relative_humidity_from_specific_humidity, relative_humidity_wet_psychrometric, saturation_equivalent_potential_temperature, saturation_mixing_ratio, @@ -36,13 +38,64 @@ vapor_pressure, vertical_totals, vertical_velocity, vertical_velocity_pressure, virtual_potential_temperature, virtual_temperature, virtual_temperature_from_dewpoint, - wet_bulb_potential_temperature, wet_bulb_temperature) + water_latent_heat_melting, water_latent_heat_sublimation, + water_latent_heat_vaporization, wet_bulb_potential_temperature, + wet_bulb_temperature) from metpy.calc.thermo import _find_append_zero_crossings, galvez_davison_index +from metpy.constants import Cp_d, Lf, Ls, Lv, Rd, T0 from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_nan, version_check) from metpy.units import is_quantity, masked_array, units +def test_moist_air_gas_constant(): + """Test calculation of gas constant for moist air.""" + q = 9 * units('g/kg') + assert_almost_equal(moist_air_gas_constant(q), 288.62 * units('J / kg / K'), 2) + assert_almost_equal(moist_air_gas_constant(0), Rd) + + +def test_moist_air_specific_heat_pressure(): + """Test calculation of specific heat for moist air.""" + q = 9 * units('g/kg') + assert_almost_equal(moist_air_specific_heat_pressure(q), 1012.36 * units('J / kg /K'), 2) + assert_almost_equal(moist_air_specific_heat_pressure(0), Cp_d) + + +def test_moist_air_poisson_exponent(): + """Test calculation of kappa for moist air.""" + q = 9 * units('g/kg') + assert_almost_equal(moist_air_poisson_exponent(q), 0.2851, 3) + assert_almost_equal(moist_air_poisson_exponent(0), kappa) + + +def test_water_latent_heat_vaporization(): + """Test temperature-dependent calculation of latent heat of vaporization for water.""" + temperature = 300 * units.K + # Divide out sig figs in results for decimal comparison + assert_almost_equal(water_latent_heat_vaporization(temperature) / 10**6, + 2.4375 * units('J / kg'), 4) + assert_almost_equal(water_latent_heat_vaporization(T0), Lv) + + +def test_water_latent_heat_sublimation(): + """Test temperature-dependent calculation of latent heat of sublimation for water.""" + temperature = 233 * units.K + # Divide out sig figs in results for decimal comparison + assert_almost_equal(water_latent_heat_sublimation(temperature) / 10**6, + 2.8438 * units('J / kg'), 4) + assert_almost_equal(water_latent_heat_sublimation(T0), Ls) + + +def test_water_latent_heat_melting(): + """Test temperature-dependent calculation of latent heat of melting for water.""" + temperature = 233 * units.K + # Divide out sig figs in results for decimal comparison + assert_almost_equal(water_latent_heat_melting(temperature) / 10**6, + 0.4192 * units('J / kg'), 4) + assert_almost_equal(water_latent_heat_melting(T0), Lf) + + def test_relative_humidity_from_dewpoint(): """Test Relative Humidity calculation.""" assert_almost_equal(relative_humidity_from_dewpoint(25. * units.degC, 15. * units.degC), From 2a38492175b3f3959666cfe90de2df79be25229c Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Thu, 26 Dec 2024 10:58:18 -0700 Subject: [PATCH 04/10] Move up random GDI test import --- tests/calc/test_thermo.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/calc/test_thermo.py b/tests/calc/test_thermo.py index 81d96174821..2fa8575fc9f 100644 --- a/tests/calc/test_thermo.py +++ b/tests/calc/test_thermo.py @@ -15,7 +15,7 @@ brunt_vaisala_period, cape_cin, ccl, cross_totals, density, dewpoint, dewpoint_from_relative_humidity, dewpoint_from_specific_humidity, downdraft_cape, dry_lapse, dry_static_energy, el, - equivalent_potential_temperature, exner_function, + equivalent_potential_temperature, exner_function, galvez_davison_index, gradient_richardson_number, InvalidSoundingError, isentropic_interpolation, isentropic_interpolation_as_dataset, k_index, lcl, lfc, lifted_index, mixed_layer, mixed_layer_cape_cin, @@ -41,8 +41,8 @@ water_latent_heat_melting, water_latent_heat_sublimation, water_latent_heat_vaporization, wet_bulb_potential_temperature, wet_bulb_temperature) -from metpy.calc.thermo import _find_append_zero_crossings, galvez_davison_index -from metpy.constants import Cp_d, Lf, Ls, Lv, Rd, T0 +from metpy.calc.thermo import _find_append_zero_crossings +from metpy.constants import Cp_d, kappa, Lf, Ls, Lv, Rd, T0 from metpy.testing import (assert_almost_equal, assert_array_almost_equal, assert_nan, version_check) from metpy.units import is_quantity, masked_array, units From 956089b90226b226ff9207313f538bf47c665199 Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Mon, 23 Dec 2024 13:45:45 -0700 Subject: [PATCH 05/10] Add to docs overrides --- docs/_templates/overrides/metpy.calc.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/_templates/overrides/metpy.calc.rst b/docs/_templates/overrides/metpy.calc.rst index e5e7a4f787f..09464d6b77f 100644 --- a/docs/_templates/overrides/metpy.calc.rst +++ b/docs/_templates/overrides/metpy.calc.rst @@ -39,6 +39,9 @@ Moist Thermodynamics mixing_ratio mixing_ratio_from_relative_humidity mixing_ratio_from_specific_humidity + moist_air_gas_constant + moist_air_poisson_exponent + moist_air_specific_heat_pressure moist_lapse moist_static_energy precipitable_water @@ -60,6 +63,9 @@ Moist Thermodynamics virtual_potential_temperature virtual_temperature virtual_temperature_from_dewpoint + water_latent_heat_melting + water_latent_heat_sublimation + water_latent_heat_vaporization wet_bulb_temperature wet_bulb_potential_temperature From 956cff064763c25101399f1c09d62a2b4af9559b Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Mon, 23 Dec 2024 15:02:46 -0700 Subject: [PATCH 06/10] Add Wiley DOI to ignore --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index f0d500c8f7f..baaf08a7645 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -455,6 +455,7 @@ # Couldn't fix these 403's with user agents r'https://doi\.org/10\.1029/2010GL045777', r'https://doi\.org/10\.1098/rspa\.2004\.1430', + r'https://doi\.org/10\.1002/qj\.3899', # Currently giving certificate errors on GitHub r'https://library.wmo.int/.*', # For some reason GHA gets a 403 from Stack Overflow From be634892a849d638b4fcb96aca4983cd8cdd54ea Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Tue, 24 Dec 2024 12:04:12 -0700 Subject: [PATCH 07/10] add necessary nounits --- src/metpy/constants/nounit.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/metpy/constants/nounit.py b/src/metpy/constants/nounit.py index 25d9f2cd228..2fd6e8a78fa 100644 --- a/src/metpy/constants/nounit.py +++ b/src/metpy/constants/nounit.py @@ -7,10 +7,17 @@ from ..units import units Rd = default.Rd.m_as('m**2 / K / s**2') +Rv = default.Rv.m_as('m**2 / K / s**2') Lv = default.Lv.m_as('m**2 / s**2') +Lf = default.Lf.m_as('m**2 / s**2') +Ls = default.Ls.m_as('m**2 / s**2') Cp_d = default.Cp_d.m_as('m**2 / K / s**2') +Cp_l = default.Cp_l.m_as('m**2 / K / s**2') +Cp_v = default.Cp_v.m_as('m**2 / K / s**2') +Cp_i = default.Cp_i.m_as('m**2 / K / s**2') zero_degc = units.Quantity(0., 'degC').m_as('K') sat_pressure_0c = default.sat_pressure_0c.m_as('Pa') epsilon = default.epsilon.m_as('') kappa = default.kappa.m_as('') g = default.g.m_as('m / s**2') +T0 = default.T0.m_as('K') From af38e9605326f305f3f28f857c41db8085c20dc4 Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Tue, 24 Dec 2024 12:04:25 -0700 Subject: [PATCH 08/10] add new physical dimensions --- src/metpy/units.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/metpy/units.py b/src/metpy/units.py index 1328c772251..cc39517ac02 100644 --- a/src/metpy/units.py +++ b/src/metpy/units.py @@ -39,7 +39,9 @@ '[temperature]': 'K', '[dimensionless]': '', '[length]': 'm', - '[speed]': 'm s**-1' + '[speed]': 'm s**-1', + '[specific_enthalpy]': 'm**2 s**-2', + '[specific_heat_capacity]': 'm**2 s**-2 K-1' } @@ -83,6 +85,8 @@ def setup_registry(reg): reg.define('degrees_east = degree = degrees_E = degreesE = degree_east = degree_E ' '= degreeE') reg.define('dBz = 1e-18 m^3; logbase: 10; logfactor: 10 = dBZ') + reg.define('[specific_enthalpy] = [energy] / [mass]') + reg.define('[specific_heat_capacity] = [specific_enthalpy] / [temperature]') # Alias geopotential meters (gpm) to just meters reg.define('@alias meter = gpm') From be2c8bcdbf360a68eb0951d9a01e4a9f8635c0a2 Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Tue, 24 Dec 2024 12:04:36 -0700 Subject: [PATCH 09/10] update to process_units --- src/metpy/calc/thermo.py | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/metpy/calc/thermo.py b/src/metpy/calc/thermo.py index 917f0f25518..bc6378538c4 100644 --- a/src/metpy/calc/thermo.py +++ b/src/metpy/calc/thermo.py @@ -31,7 +31,9 @@ @exporter.export @preprocess_and_wrap(wrap_like='specific_humidity') -@check_units('[dimensionless]') +@process_units(input_dimensionalities={'specific_humidity': 'dimensionless'}, + output_dimensionalities='[specific_heat_capacity]', + output_to='J K**-1 kg**-1 ') def moist_air_gas_constant(specific_humidity): r"""Calculate R_m, the specific gas constant for a parcel of moist air. @@ -62,13 +64,15 @@ def moist_air_gas_constant(specific_humidity): Eq 16, [Romps2017]_ using MetPy-defined constants in place of cited values. """ - return ((1 - specific_humidity) * mpconsts.dry_air_gas_constant - + specific_humidity * mpconsts.water_gas_constant) + return ((1 - specific_humidity) * mpconsts.nounit.Rd + + specific_humidity * mpconsts.nounit.Rv) @exporter.export @preprocess_and_wrap(wrap_like='specific_humidity') -@check_units('[dimensionless]') +@process_units(input_dimensionalities={'specific_humidity': 'dimensionless'}, + output_dimensionalities='[specific_heat_capacity]', + output_to='J K**-1 kg**-1 ') def moist_air_specific_heat_pressure(specific_humidity): r"""Calculate C_pm, the specific heat at constant pressure for a moist air parcel. @@ -99,8 +103,8 @@ def moist_air_specific_heat_pressure(specific_humidity): Eq 17, [Romps2017]_ using MetPy-defined constants in place of cited values. """ - return ((1 - specific_humidity) * mpconsts.dry_air_spec_heat_press - + specific_humidity * mpconsts.wv_specific_heat_press) + return ((1 - specific_humidity) * mpconsts.nounit.Cp_d + + specific_humidity * mpconsts.nounit.Cp_v) @exporter.export @@ -139,7 +143,9 @@ def moist_air_poisson_exponent(specific_humidity): @exporter.export @preprocess_and_wrap(wrap_like='temperature') -@check_units('[temperature]') +@process_units(input_dimensionalities={'temperature': '[temperature]'}, + output_dimensionalities='[specific_enthalpy]', + output_to='J kg**-1') def water_latent_heat_vaporization(temperature): r"""Calculate the latent heat of vaporization for water. @@ -175,14 +181,16 @@ def water_latent_heat_vaporization(temperature): Eq 15, [Ambaum2020]_, using MetPy-defined constants in place of cited values. """ - return (mpconsts.water_heat_vaporization - - (mpconsts.water_specific_heat - mpconsts.wv_specific_heat_press) - * (temperature - mpconsts.water_triple_point_temperature)) + return (mpconsts.nounit.Lv + - (mpconsts.nounit.Cp_l - mpconsts.nounit.Cp_v) + * (temperature - mpconsts.nounit.T0)) @exporter.export @preprocess_and_wrap(wrap_like='temperature') -@check_units('[temperature]') +@process_units(input_dimensionalities={'temperature': '[temperature]'}, + output_dimensionalities='[specific_enthalpy]', + output_to='J kg**-1') def water_latent_heat_sublimation(temperature): r"""Calculate the latent heat of sublimation for water. @@ -215,14 +223,16 @@ def water_latent_heat_sublimation(temperature): Eq 18, [Ambaum2020]_, using MetPy-defined constants in place of cited values. """ - return (mpconsts.water_heat_sublimation - - (mpconsts.ice_specific_heat - mpconsts.wv_specific_heat_press) - * (temperature - mpconsts.water_triple_point_temperature)) + return (mpconsts.nounit.Ls + - (mpconsts.nounit.Cp_i - mpconsts.nounit.Cp_v) + * (temperature - mpconsts.nounit.T0)) @exporter.export @preprocess_and_wrap(wrap_like='temperature') -@check_units('[temperature]') +@process_units(input_dimensionalities={'temperature': '[temperature]'}, + output_dimensionalities='[specific_enthalpy]', + output_to='J kg**-1') def water_latent_heat_melting(temperature): r"""Calculate the latent heat of melting for water. @@ -256,9 +266,9 @@ def water_latent_heat_melting(temperature): Uses MetPy-defined constants in place of cited values. """ - return (mpconsts.water_heat_fusion - - (mpconsts.water_specific_heat - mpconsts.ice_specific_heat) - * (temperature - mpconsts.water_triple_point_temperature)) + return (mpconsts.nounit.Lf + - (mpconsts.nounit.Cp_l - mpconsts.nounit.Cp_i) + * (temperature - mpconsts.nounit.T0)) @exporter.export From 26ea138d8d16c5c98187c803ec6694d345140a26 Mon Sep 17 00:00:00 2001 From: Drew Camron Date: Fri, 3 Jan 2025 12:22:39 -0700 Subject: [PATCH 10/10] Reduce array ops --- src/metpy/calc/thermo.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/metpy/calc/thermo.py b/src/metpy/calc/thermo.py index bc6378538c4..48f18d4a6fa 100644 --- a/src/metpy/calc/thermo.py +++ b/src/metpy/calc/thermo.py @@ -59,13 +59,14 @@ def moist_air_gas_constant(specific_humidity): Notes ----- + Adapted from + .. math:: R_m = (1 - q_v) R_a + q_v R_v Eq 16, [Romps2017]_ using MetPy-defined constants in place of cited values. """ - return ((1 - specific_humidity) * mpconsts.nounit.Rd - + specific_humidity * mpconsts.nounit.Rv) + return mpconsts.nounit.Rd + specific_humidity * (mpconsts.nounit.Rv - mpconsts.nounit.Rd) @exporter.export @@ -98,13 +99,15 @@ def moist_air_specific_heat_pressure(specific_humidity): Notes ----- + Adapted from + .. math:: c_{pm} = (1 - q_v) c_{pa} + q_v c_{pv} Eq 17, [Romps2017]_ using MetPy-defined constants in place of cited values. """ - return ((1 - specific_humidity) * mpconsts.nounit.Cp_d - + specific_humidity * mpconsts.nounit.Cp_v) + return (mpconsts.nounit.Cp_d + + specific_humidity * (mpconsts.nounit.Cp_v - mpconsts.nounit.Cp_d)) @exporter.export