|
1
|
|
|
# Copyright (c) 2008-2015 MetPy Developers. |
|
2
|
|
|
# Distributed under the terms of the BSD 3-Clause License. |
|
3
|
|
|
# SPDX-License-Identifier: BSD-3-Clause |
|
4
|
|
|
"""Test the `thermo` module.""" |
|
5
|
|
|
|
|
6
|
|
|
import numpy as np |
|
7
|
|
|
import pytest |
|
8
|
|
|
|
|
9
|
|
|
from metpy.calc import (dewpoint, dewpoint_rh, dry_lapse, equivalent_potential_temperature, |
|
10
|
|
|
lcl, lfc, mixing_ratio, moist_lapse, parcel_profile, |
|
11
|
|
|
potential_temperature, saturation_mixing_ratio, |
|
12
|
|
|
saturation_vapor_pressure, vapor_pressure) |
|
13
|
|
|
from metpy.testing import assert_almost_equal, assert_array_almost_equal |
|
14
|
|
|
from metpy.units import units |
|
15
|
|
|
|
|
16
|
|
|
|
|
17
|
|
|
def test_potential_temperature(): |
|
18
|
|
|
"""Test potential_temperature calculation.""" |
|
19
|
|
|
temp = np.array([278., 283., 291., 298.]) * units.kelvin |
|
20
|
|
|
pres = np.array([900., 500., 300., 100.]) * units.mbar |
|
21
|
|
|
real_th = np.array([286.493, 344.961, 410.4335, 575.236]) * units.kelvin |
|
22
|
|
|
assert_array_almost_equal(potential_temperature(pres, temp), real_th, 3) |
|
23
|
|
|
|
|
24
|
|
|
|
|
25
|
|
|
def test_scalar(): |
|
26
|
|
|
"""Test potential_temperature accepts scalar values.""" |
|
27
|
|
|
assert_almost_equal(potential_temperature(1000. * units.mbar, 293. * units.kelvin), |
|
28
|
|
|
293. * units.kelvin, 4) |
|
29
|
|
|
assert_almost_equal(potential_temperature(800. * units.mbar, 293. * units.kelvin), |
|
30
|
|
|
312.2828 * units.kelvin, 4) |
|
31
|
|
|
|
|
32
|
|
|
|
|
33
|
|
|
def test_fahrenheit(): |
|
34
|
|
|
"""Test that potential_temperature handles temperature values in Fahrenheit.""" |
|
35
|
|
|
assert_almost_equal(potential_temperature(800. * units.mbar, 68. * units.degF), |
|
36
|
|
|
(312.444 * units.kelvin).to(units.degF), 2) |
|
37
|
|
|
|
|
38
|
|
|
|
|
39
|
|
|
def test_pot_temp_inhg(): |
|
40
|
|
|
"""Test that potential_temperature can handle pressure not in mb (issue #165).""" |
|
41
|
|
|
assert_almost_equal(potential_temperature(29.92 * units.inHg, 29 * units.degC), |
|
42
|
|
|
301.019735 * units.kelvin, 4) |
|
43
|
|
|
|
|
44
|
|
|
|
|
45
|
|
|
def test_dry_lapse(): |
|
46
|
|
|
"""Test dry_lapse calculation.""" |
|
47
|
|
|
levels = np.array([1000, 900, 864.89]) * units.mbar |
|
48
|
|
|
temps = dry_lapse(levels, 303.15 * units.kelvin) |
|
49
|
|
|
assert_array_almost_equal(temps, |
|
50
|
|
|
np.array([303.15, 294.16, 290.83]) * units.kelvin, 2) |
|
51
|
|
|
|
|
52
|
|
|
|
|
53
|
|
|
def test_dry_lapse_2_levels(): |
|
54
|
|
|
"""Test dry_lapse calculation when given only two levels.""" |
|
55
|
|
|
temps = dry_lapse(np.array([1000., 500.]) * units.mbar, 293. * units.kelvin) |
|
56
|
|
|
assert_array_almost_equal(temps, [293., 240.3723] * units.kelvin, 4) |
|
57
|
|
|
|
|
58
|
|
|
|
|
59
|
|
|
def test_moist_lapse(): |
|
60
|
|
|
"""Test moist_lapse calculation.""" |
|
61
|
|
|
temp = moist_lapse(np.array([1000., 800., 600., 500., 400.]) * units.mbar, |
|
62
|
|
|
293. * units.kelvin) |
|
63
|
|
|
true_temp = np.array([293, 284.64, 272.81, 264.42, 252.91]) * units.kelvin |
|
64
|
|
|
assert_array_almost_equal(temp, true_temp, 2) |
|
65
|
|
|
|
|
66
|
|
|
|
|
67
|
|
|
def test_moist_lapse_degc(): |
|
68
|
|
|
"""Test moist_lapse with Celsius temperatures.""" |
|
69
|
|
|
temp = moist_lapse(np.array([1000., 800., 600., 500., 400.]) * units.mbar, |
|
70
|
|
|
19.85 * units.degC) |
|
71
|
|
|
true_temp = np.array([293, 284.64, 272.81, 264.42, 252.91]) * units.kelvin |
|
72
|
|
|
assert_array_almost_equal(temp, true_temp, 2) |
|
73
|
|
|
|
|
74
|
|
|
|
|
75
|
|
|
def test_parcel_profile(): |
|
76
|
|
|
"""Test parcel profile calculation.""" |
|
77
|
|
|
levels = np.array([1000., 900., 800., 700., 600., 500., 400.]) * units.mbar |
|
78
|
|
|
true_prof = np.array([303.15, 294.16, 288.026, 283.073, 277.058, 269.402, |
|
79
|
|
|
258.966]) * units.kelvin |
|
80
|
|
|
|
|
81
|
|
|
prof = parcel_profile(levels, 30. * units.degC, 20. * units.degC) |
|
82
|
|
|
assert_array_almost_equal(prof, true_prof, 2) |
|
83
|
|
|
|
|
84
|
|
|
|
|
85
|
|
|
def test_parcel_profile_saturated(): |
|
86
|
|
|
"""Test parcel_profile works when LCL in levels (issue #232).""" |
|
87
|
|
|
levels = np.array([1000., 700., 500.]) * units.mbar |
|
88
|
|
|
true_prof = np.array([296.95, 284.381, 271.123]) * units.kelvin |
|
89
|
|
|
|
|
90
|
|
|
prof = parcel_profile(levels, 23.8 * units.degC, 23.8 * units.degC) |
|
91
|
|
|
assert_array_almost_equal(prof, true_prof, 2) |
|
92
|
|
|
|
|
93
|
|
|
|
|
94
|
|
|
def test_sat_vapor_pressure(): |
|
95
|
|
|
"""Test saturation_vapor_pressure calculation.""" |
|
96
|
|
|
temp = np.array([5., 10., 18., 25.]) * units.degC |
|
97
|
|
|
real_es = np.array([8.72, 12.27, 20.63, 31.67]) * units.mbar |
|
98
|
|
|
assert_array_almost_equal(saturation_vapor_pressure(temp), real_es, 2) |
|
99
|
|
|
|
|
100
|
|
|
|
|
101
|
|
|
def test_sat_vapor_pressure_scalar(): |
|
102
|
|
|
"""Test saturation_vapor_pressure handles scalar values.""" |
|
103
|
|
|
es = saturation_vapor_pressure(0 * units.degC) |
|
104
|
|
|
assert_almost_equal(es, 6.112 * units.mbar, 3) |
|
105
|
|
|
|
|
106
|
|
|
|
|
107
|
|
|
def test_sat_vapor_pressure_fahrenheit(): |
|
108
|
|
|
"""Test saturation_vapor_pressure handles temperature in Fahrenheit.""" |
|
109
|
|
|
temp = np.array([50., 68.]) * units.degF |
|
110
|
|
|
real_es = np.array([12.2717, 23.3695]) * units.mbar |
|
111
|
|
|
assert_array_almost_equal(saturation_vapor_pressure(temp), real_es, 4) |
|
112
|
|
|
|
|
113
|
|
|
|
|
114
|
|
|
def test_basic_dewpoint_rh(): |
|
115
|
|
|
"""Test dewpoint_rh function.""" |
|
116
|
|
|
temp = np.array([30., 25., 10., 20., 25.]) * units.degC |
|
117
|
|
|
rh = np.array([30., 45., 55., 80., 85.]) / 100. |
|
118
|
|
|
|
|
119
|
|
|
real_td = np.array([11, 12, 1, 16, 22]) * units.degC |
|
120
|
|
|
assert_array_almost_equal(real_td, dewpoint_rh(temp, rh), 0) |
|
121
|
|
|
|
|
122
|
|
|
|
|
123
|
|
|
def test_scalar_dewpoint_rh(): |
|
124
|
|
|
"""Test dewpoint_rh with scalar values.""" |
|
125
|
|
|
td = dewpoint_rh(10.6 * units.degC, 0.37) |
|
126
|
|
|
assert_almost_equal(td, 26. * units.degF, 0) |
|
127
|
|
|
|
|
128
|
|
|
|
|
129
|
|
|
def test_dewpoint(): |
|
130
|
|
|
"""Test dewpoint calculation.""" |
|
131
|
|
|
assert_almost_equal(dewpoint(6.112 * units.mbar), 0. * units.degC, 2) |
|
132
|
|
|
|
|
133
|
|
|
|
|
134
|
|
|
def test_dewpoint_weird_units(): |
|
135
|
|
|
"""Test dewpoint using non-standard units. |
|
136
|
|
|
|
|
137
|
|
|
Revealed from odd dimensionless units and ending up using numpy.ma math |
|
138
|
|
|
functions instead of numpy ones. |
|
139
|
|
|
""" |
|
140
|
|
|
assert_almost_equal(dewpoint(15825.6 * units('g * mbar / kg')), |
|
141
|
|
|
13.8564 * units.degC, 4) |
|
142
|
|
|
|
|
143
|
|
|
|
|
144
|
|
|
def test_mixing_ratio(): |
|
145
|
|
|
"""Test mixing ratio calculation.""" |
|
146
|
|
|
p = 998. * units.mbar |
|
147
|
|
|
e = 73.75 * units.mbar |
|
148
|
|
|
assert_almost_equal(mixing_ratio(e, p), 0.04963, 2) |
|
149
|
|
|
|
|
150
|
|
|
|
|
151
|
|
|
def test_vapor_pressure(): |
|
152
|
|
|
"""Test vapor pressure calculation.""" |
|
153
|
|
|
assert_almost_equal(vapor_pressure(998. * units.mbar, 0.04963), |
|
154
|
|
|
73.74925 * units.mbar, 5) |
|
155
|
|
|
|
|
156
|
|
|
|
|
157
|
|
|
def test_lcl(): |
|
158
|
|
|
"""Test LCL calculation.""" |
|
159
|
|
|
lcl_pressure, lcl_temperature = lcl(1000. * units.mbar, 30. * units.degC, 20. * units.degC) |
|
160
|
|
|
assert_almost_equal(lcl_pressure, 864.761 * units.mbar, 2) |
|
161
|
|
|
assert_almost_equal(lcl_temperature, 17.676 * units.degC, 2) |
|
162
|
|
|
|
|
163
|
|
|
|
|
164
|
|
|
def test_lcl_convergence(): |
|
165
|
|
|
"""Test LCL calculation convergence failure.""" |
|
166
|
|
|
with pytest.raises(RuntimeError): |
|
167
|
|
|
lcl(1000. * units.mbar, 30. * units.degC, 20. * units.degC, max_iters=2) |
|
168
|
|
|
|
|
169
|
|
|
|
|
170
|
|
|
def test_lfc_basic(): |
|
171
|
|
|
"""Test LFC calculation.""" |
|
172
|
|
|
levels = np.array([959., 779.2, 751.3, 724.3, 700., 269.]) * units.mbar |
|
173
|
|
|
temperatures = np.array([22.2, 14.6, 12., 9.4, 7., -49.]) * units.celsius |
|
174
|
|
|
dewpoints = np.array([19., -11.2, -10.8, -10.4, -10., -53.2]) * units.celsius |
|
175
|
|
|
l = lfc(levels, temperatures, dewpoints) |
|
176
|
|
|
assert_almost_equal(l[0], 727.468 * units.mbar, 2) |
|
177
|
|
|
assert_almost_equal(l[1], 9.705 * units.celsius, 2) |
|
178
|
|
|
|
|
179
|
|
|
|
|
180
|
|
|
def test_saturation_mixing_ratio(): |
|
181
|
|
|
"""Test saturation mixing ratio calculation.""" |
|
182
|
|
|
p = 999. * units.mbar |
|
183
|
|
|
t = 288. * units.kelvin |
|
184
|
|
|
assert_almost_equal(saturation_mixing_ratio(p, t), .01068, 3) |
|
185
|
|
|
|
|
186
|
|
|
|
|
187
|
|
|
def test_equivalent_potential_temperature(): |
|
188
|
|
|
"""Test equivalent potential temperature calculation.""" |
|
189
|
|
|
p = 999. * units.mbar |
|
190
|
|
|
t = 288. * units.kelvin |
|
191
|
|
|
ept = equivalent_potential_temperature(p, t) |
|
192
|
|
|
assert_almost_equal(ept, 315.9548 * units.kelvin, 3) |
|
193
|
|
|
|