IODA Bundle
meteo_utils.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 #
4 # (C) Copyright 2020 UCAR
5 #
6 # This software is licensed under the terms of the Apache Licence Version 2.0
7 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
8 #
9 # author: Greg Thompson gthompsn AT ucar DOT edu
10 #
11 
12 import math
13 
14 
15 class meteo_utils(object):
16 
17  # Constructor
18  def __init__(self):
19  # Define some constants used for some variable conversions
20 
21  self.PIPI = math.pi
22  self.DEG_2_RADDEG_2_RAD = self.PIPI/180.
23  self.C_2_KC_2_K = 273.15 # Zero Centigrade to Kelvin
24  self.MS_2_KTSMS_2_KTS = 1.94 # Meters per second to knots
25  self.KTS_2_MSKTS_2_MS = 1./self.MS_2_KTSMS_2_KTS # Knots to meters per second
26  self.inHg_2_PainHg_2_Pa = 3386.39 # Inches of mercury to Pascals
27  self.STP_PSTP_P = 29.92 * self.inHg_2_PainHg_2_Pa # Standard pressure, convert to Pascals
28  self.STP_TSTP_T = 15.0 + self.C_2_KC_2_K # Standard temperature, convert to Kelvin
29  self.STP_rho_airSTP_rho_air = self.STP_PSTP_P/(287.0*self.STP_TSTP_T) # Air density at STP (standard temperature and pressure)
30  self.FT_2_MFT_2_M = 0.3048 # Feet to meters
31  self.RR = 287.04 # Gas constant
32  self.CpCp = 1004.0 # Specific heat capacity
33  self.LAPSELAPSE = 0.0065 # Standard lapse rate 6.5C per kilometer
34  self.gg = 9.80665 # Gravity (m/s^2)
35  self.RHO_WATERRHO_WATER = 1000. # Density of water (kg/m^3)
36 #
37 # --+---+---+---+
38 
39  def specific_humidity(self, dewp_K, pres_Pa):
40 
41  '''
42  Function to compute specific humidity
43  Required input variables are dewpoint (K) and surface pressure (Pa)
44  From dewpoint a simple water vapor mixing ratio is calculated, then
45  specific humidity is r/(1.+r)
46  '''
47 
48  r = self.r_sub_sr_sub_s(pres_Pa, dewp_K)
49  sh = r / (1.0 + r)
50 
51  return sh
52 #
53 # --+---+---+---+
54 
55  def r_sub_s(self, pres_Pa, temp_K):
56 
57  '''
58  compute saturation mixing ratio (kg/kg) by calling function
59  to calculate saturation vapor pressure over water.
60  pres_Pa - pressure (pa)
61  temp_K - temperature (k)
62  '''
63 
64  es = self.e_sub_se_sub_s(temp_K)
65 
66  # Even at P=1050hPa and T=55C, sat. vap. pres only contributes to ~15% of total pressure.
67  # The following MIN statement is needed for insanely high altitude global model tops like 1hPa.
68 
69  es = min(es, pres_Pa*0.15)
70 
71  rs = 0.622*es/(pres_Pa-es)
72 
73  return rs
74 #
75 # --+---+---+---+
76 
77  def e_sub_s(self, temp_K):
78 
79  '''
80  compute saturation vapor pressure (Pa) over liquid with
81  polynomial fit of Goff-Gratch (1946) formulation. (Walko, 1991)
82  '''
83 
84  c = [610.5851, 44.40316, 1.430341, 0.2641412e-1, 0.2995057e-3, 0.2031998e-5, 0.6936113e-8, 0.2564861e-11, -0.3704404e-13]
85  x = max(-80., temp_K-self.C_2_KC_2_K)
86  es = c[0]+x*(c[1]+x*(c[2]+x*(c[3]+x*(c[4]+x*(c[5]+x*(c[6]+x*(c[7]+x*c[8])))))))
87 
88  '''
89  ALTERNATIVE (costs more CPU, more accurate than Walko, 1991)
90  Source: Murphy and Koop, Review of the vapour pressure of ice and
91  supercooled water for atmospheric applications, Q. J. R.
92  Meteorol. Soc (2005), 131, pp. 1539-1565.
93 
94  es = math.exp(54.842763 - 6763.22 / temp_K - 4.210 * math.alog(temp_K) + 0.000367 * temp_K
95  + math.tanh(0.0415 * (temp_K - 218.8)) * (53.878 - 1331.22
96  / temp_K - 9.44523 * math.alog(temp_K) + 0.014025 * temp_K))
97 
98  ALTERNATIVE: Classical formula from Rogers and Yau (1989; Eq2.17)
99 
100  es = 1000.*0.6112*math.exp(17.67*(temp_K-self.C_2_K)/(temp_K-29.65))
101  '''
102 
103  return es
104 #
105 # --+---+---+---+
106 
107  def r_sub_i(self, pres_Pa, temp_K):
108 
109  '''
110  compute saturation mixing ratio (kg/kg) by calling function
111  to calculate saturation vapor pressure over ice.
112  pres_Pa - pressure (pa)
113  temp_K - temperature (k)
114  '''
115 
116  esi = self.e_sub_ie_sub_i(temp_K)
117 
118  # Even at P=1050hPa and T=55C, sat. vap. pres only contributes to ~15% of total pressure.
119  # The following MIN statement is needed for insanely high altitude global model tops like 1hPa.
120 
121  esi = min(esi, pres_Pa*0.15)
122 
123  ri = 0.622*esi/(pres_Pa-esi)
124 
125  return ri
126 #
127 # --+---+---+---+
128 
129  def e_sub_i(self, temp_K):
130 
131  '''
132  compute saturation vapor pressure (Pa) over ice with
133  polynomial fit of Goff-Gratch (1946) formulation. (Walko, 1991)
134  '''
135 
136  c = [.609868993E03, .499320233E02, .184672631E01, .402737184E-1, .565392987E-3, .521693933E-5, .307839583E-7, .105785160E-9, .161444444E-12]
137  x = max(-80., temp_K-self.C_2_KC_2_K)
138  esi = c[0]+x*(c[1]+x*(c[2]+x*(c[3]+x*(c[4]+x*(c[5]+x*(c[6]+x*(c[7]+x*c[8])))))))
139 
140  '''
141  ALTERNATIVE (costs more CPU, more accurate than Walko, 1991)
142  Source: Murphy and Koop, Review of the vapour pressure of ice and
143  supercooled water for atmospheric applications, Q. J. R.
144  Meteorol. Soc (2005), 131, pp. 1539-1565.
145 
146  esi = math.exp(9.550426 - 5723.265/temp_K + 3.53068*math.alog(temp_K) - 0.00728332*temp_K)
147 
148  ALTERNATIVE from Rogers and Yau (1989; Eq2.17)
149 
150  esi = 1000.*0.6112*math.exp(21.8745584*(temp_K-self.C_2_K)/(temp_K-7.66))
151  '''
152 
153  return esi
154 #
155 # --+---+---+---+
156 
157  def std_atmos(self, pres_Pa):
158 
159  '''
160  standard atmos height in meters is returned for given p in Pascals
161  '''
162  pr = pres_Pa*0.01
163  height = 44307.692 * (1.0 - (pr/1013.25)**0.190)
164 
165  return height
166 #
167 # --+---+---+---+
168 
169  def std_atmos_p(self, height):
170 
171  '''
172  standard atmos pressure in Pascals is returned for given height in meters
173  '''
174 
175  pr = math.exp(math.log(1.0-height/44307.692)/0.19)*101325.0
176 
177  return pr
178 #
179 # --+---+---+---+
180 
181  def precipitable_water(self, pres_Pa, w_non):
182 
183  '''
184  Input is a column ordered with lowest index is physically lower in the atmosphere (pressure decreasing as the index increases).
185  pres_Pa = Pressure in Pascals
186  w_non = mixing ratio (non-dimensional = kg/kg)
187  returned precipitable water value in meters only below 150mb
188  '''
189 
190  sum = 0.
191  k = 1
192  while k < len(pres_Pa):
193  if (pres_Pa[k] > 15000.0):
194  sum = sum + ((w_non[k]+w_non[k-1])*0.5) * abs(pres_Pa[k]-pres_Pa[k-1])
195  k = k + 1
196 
197  answer = sum/(self.gg*self.RHO_WATERRHO_WATER)
198 
199  return answer
200 #
201 # --+---+---+---+
202 
203  def dir_speed_2_uv(self, wdir, wspd):
204 
205  '''
206  From wind direction and speed, compute u,v wind components
207  '''
208 
209  u = -wspd * math.sin(wdir*self.DEG_2_RADDEG_2_RAD)
210  v = -wspd * math.cos(wdir*self.DEG_2_RADDEG_2_RAD)
211 
212  return u, v
213 #
214 # --+---+---+---+
215 
216  def altim_2_sfcPressure(self, altim, elev):
217 
218  '''
219  From altimeter (inches of mercury, Hg), and station elevation (m),
220  compute surface pressure returned in Pascals.
221  '''
222 
223  psfc = altim * ((self.STP_TSTP_T - self.LAPSELAPSE*elev) / self.STP_TSTP_T)**5.2561
224  psfc = psfc * self.inHg_2_PainHg_2_Pa
225 
226  return psfc
227 #
228 # --+---+---+---+
229 
230  def theta_e(self, pres_Pa, temp_K, w_non, tlcl_K):
231 
232  '''
233  The following code was based on Bolton (1980) eqn #43
234  and claims to have 0.3 K maximum error within -35 < T < 35 C
235  pres_Pa = Pressure in Pascals
236  temp_K = Temperature in Kelvin
237  w_non = mixing ratio (non-dimensional = kg/kg)
238  tlcl_K = Temperature at Lifting Condensation Level (K)
239  '''
240 
241  pp = pres_Pa
242  tt = temp_K
243  rr = w_non + 1.e-8
244  tlc = tlcl_K
245 
246  power = (0.2854*(1.0 - (0.28*rr)))
247  xx = tt * (100000.0/pp)**power
248 
249  p1 = (3.376/tlc) - 0.00254
250  p2 = (rr*1000.0) * (1.0 + 0.81*rr)
251 
252  thetae = xx * math.exp(p1*p2)
253 
254  return thetae
255 #
256 # --+---+---+---+
257 
258  def t_lcl(self, temp_K, tdew_K):
259 
260  '''
261  The following code was based on Bolton (1980) eqn #15
262  and claims to have 0.1 K maximum error within -35 < T < 35 C
263  temp_K = Temperature in Kelvin
264  tdew_K = Dewpoint T at Lifting Condensation Level (K)
265  '''
266 
267  tt = temp_K
268  tttd = tdew_K
269  denom = (1.0/(tttd-56.0)) + (math.log(tt/tttd)/800.)
270  tlcl = (1.0/denom) + 56.0
271 
272  return tlcl
273 #
274 # --+---+---+---+
275 
276  def t_dew(self, pres_Pa, w_non):
277 
278  '''
279  I cannot recall the original source of this function
280  pres_Pa = Pressure in Pascals
281  w_non = mixing ratio (non-dimensional = kg/kg)
282  '''
283 
284  p = pres_Pa
285  rr = w_non+1e-8
286  es = p*rr/(.622+rr)
287  esln = math.log(es)
288  tdew = (35.86*esln-4947.2325)/(esln-23.6837)
289 
290  return tdew
291 #
292 # --+---+---+---+
293 
294  def theta_wetb(self, thetae_K):
295 
296  '''
297  Eqn below was gotten from polynomial fit to data in
298  Smithsonian Meteorological Tables showing Theta-e
299  and Theta-w
300  '''
301 
302  c = [-1.00922292e-10, -1.47945344e-8, -1.7303757e-6, -0.00012709, 1.15849867e-6, -3.518296861e-9, 3.5741522e-12]
303  d = [0.0, -3.5223513e-10, -5.7250807e-8, -5.83975422e-6, 4.72445163e-8, -1.13402845e-10, 8.729580402e-14]
304 
305  x = min(475.0, thetae_K)
306 
307  if (x <= 335.5):
308  answer = c(0)+x*(c(1)+x*(c(2)+x*(c(3)+x*(c(4)+x*(c(5)+x*c(6))))))
309  else:
310  answer = d(0)+x*(d(1)+x*(d(2)+x*(d(3)+x*(d(4)+x*(d(5)+x*d(6))))))
311 
312  th_wetb = answer + self.C_2_KC_2_K
313 
314  return th_wetb
315 #
316 # --+---+---+---+
317 
318  def compT_fr_The(self, thelcl_K, pres_Pa):
319 
320  '''
321  pres_Pa = Pressure in Pascals
322  thelcl = Theta-e at LCL (units in Kelvin)
323  Temperature (K) is returned given Theta-e at LCL
324  and a pressure. This describes a moist-adiabat.
325  This temperature is the parcel temp at level Pres
326  along moist adiabat described by theta-e.
327  '''
328 
329  guess = (thelcl_K - 0.5 * (max(thelcl_K-270., 0.))**1.05) * (pres_Pa/100000.0)**.2
330  epsilon = 0.01
331 
332  iter = 1
333  while iter < 100:
334  w1 = self.r_sub_sr_sub_s(pres_Pa, guess)
335  w2 = self.r_sub_sr_sub_s(pres_Pa, guess+1.)
336  tenu = self.theta_etheta_e(pres_Pa, guess, w1, guess)
337  tenup = self.theta_etheta_e(pres_Pa, guess+1, w2, guess+1.)
338  cor = (thelcl_K - tenu) / (tenup - tenu)
339  guess = guess + cor
340  if((cor < epsilon) and (-cor < epsilon)):
341  answer = guess
342  return answer
343  # endif
344 
345  iter = iter + 1
346  # endwhile
347 
348  '''
349  If we come out of the iteration without a solution, then it did not converge. This is not good.
350  '''
351 
352  print("WARNING: solution did not converge in compT_fr_The, " + str(thelcl_K) + ", " + str(pres_Pa))
353 
354  thwlcl_K = self.theta_wetbtheta_wetb(thelcl_K)
355  answer = thwlcl_K*((pres_Pa/100000.0)**0.286)
356  return answer
357 #
358 # --+---+---+---+
def t_lcl(self, temp_K, tdew_K)
Definition: meteo_utils.py:258
def r_sub_s(self, pres_Pa, temp_K)
Definition: meteo_utils.py:55
def dir_speed_2_uv(self, wdir, wspd)
Definition: meteo_utils.py:203
def e_sub_i(self, temp_K)
Definition: meteo_utils.py:129
def std_atmos_p(self, height)
Definition: meteo_utils.py:169
def compT_fr_The(self, thelcl_K, pres_Pa)
Definition: meteo_utils.py:318
def specific_humidity(self, dewp_K, pres_Pa)
Definition: meteo_utils.py:39
def theta_e(self, pres_Pa, temp_K, w_non, tlcl_K)
Definition: meteo_utils.py:230
def theta_wetb(self, thetae_K)
Definition: meteo_utils.py:294
def precipitable_water(self, pres_Pa, w_non)
Definition: meteo_utils.py:181
def altim_2_sfcPressure(self, altim, elev)
Definition: meteo_utils.py:216
def r_sub_i(self, pres_Pa, temp_K)
Definition: meteo_utils.py:107
def e_sub_s(self, temp_K)
Definition: meteo_utils.py:77
def t_dew(self, pres_Pa, w_non)
Definition: meteo_utils.py:276
def std_atmos(self, pres_Pa)
Definition: meteo_utils.py:157